当前位置: 首页 > news >正文

main函数,常量指针与指针常量,野指针等,void与void的区别

指针(续)

main函数原型

定义

main函数有多种定义格式,main函数也是函数,函数相关的结论对main函数也有效。

main函数的完整写法:

 int main(int argc, char *argv[]){..}int main(int argc, char **argv){..}

扩展写法:

 main(){}  等价 int main(){}   // C11之后不再支持 缺省 返回类型int main(void){}   等价 int main(){}void main(void){}  等价 void main(){}int main(int a){}int main(int a, int b, int c){}...

说明

① argc,argv是形参,他们俩可以修改

② main函数的扩展写法有些编译器不支持,编译报警告

③ argc和argv的常规写法

  • argc:存储了参数的个数,默认是1个,也就是运行程序的名字
  • argv:存储了所有参数的字符串形式

④ main函数是系统通过函数指针的回调调用。

演示

代码:

#include <stdio.h>int main(int argc, char **argv)  // {"abc","aaa"}   对行地址解引用,得到首列地址
{// 访问参数个数 argcprintf("argc=%d\n", argc);// 遍历参数(每一个参数都是一个字符串常量)for(int i=0;i< argc; i++){printf("%s,%s\n", argv[i], *(argv+i));}printf("\n");
}

运行结果:

在这里插入图片描述

在这里插入图片描述

常量指针与指针常量

常量类型

① 字面量:直接使用固定值(如:12,hello,orange, 杨家辉三角),符号常量和枚举在编译器转换为了字面量

② 只读常量:用const修饰的变量,初始化之后不可修改。

const int a = 10;  // 只读常量
a = 21;  // 编译报错

常量指针

  • 本质:指向常量数据的指针

  • 语法:

    const 数据类型 *变量名;
    const 数据类型* 变量名;
    
  • 举例:

    const int *p;   // p是常量指针
    
  • 特性:

    • 指向对象的数据不可改变(int a = 10; const int *p = &a; *p = 20;,非法)
    • 指针本身的指向可以改变(int a = 10, b = 20; const int *p = &a; p = &b;,合法)
  • 案例:

    #include <stdio.h>int main()
    {int a = 10;        // 变量const int *p = &a; // 常量指针// *p = 100;       // 错误,指针指向的数据不可改变printf("%d\n", *p);// 10int b = 20;        // 变量p = &b;            // 正确,指针指向可以改变printf("%d\n", *p);// 20
    }
    

指针常量

  • 本质:指针本身是常量,指向固定地址

  • 语法:

    数据类型* const 变量名;
    数据类型 *const 变量名;
    
  • 特性:

    • 指向对象的数据可以改变(int a = 10; int* const p = &a; *p = 20;,合法)
    • 指针本身的指向不可改变(int a = 10, b = 20; int* const p = &a; p = &b;,非法)
  • 注意:

    定义时必须初始化:

    int a = 10;
    int* const p = &a;  // 正确
    
  • 案例:

    #include <stdio.h>int main()
    {int a = 10;        // 变量int* const p = &a; // 指针常量*p = 100;          // 正确,指针指向的数据可以改变printf("%d\n", *p);// 100int b = 20;        // 变量// p = &b;            // 错误,指针指向不可改变printf("%d\n", *p);// 100
    }
    

常量指针常量

  • 本质:指针指向和指向对象的数据都不可改变

  • 语法:

    const 数据类型* const 变量名;
    const 数据类型 *const 变量名;
    
  • 举例:

    const int* const p;  // p是常量指针常量
    
  • 特性:

    • 指向对象的数据不可改变(int a = 10; int* const p = &a; *p = 20;,非法)
    • 指针本身的指向不可改变(int a = 10, b = 20; int* const p = &a; p = &b;,非法)
  • 注意:

    定义时需要初始化:

    int a = 10;
    const int *const p = &a; // 正确
    

    简单理解:不管是常量指针、指针常量还是常量指针常量,本质上都是一个赋值受到限制的指针变量。

总结对比

类型语法指向可变数据可变
常量指针const int *p✔️
指针常量int *const p✔️
常量指针常量const int *const p

关键点

  1. const* 左侧:修饰数据(常量指针)
  2. const* 右侧:修饰指针(指针常量)
  3. 函数参数优先使用常量指针,提高代码安全性
  4. 指针常量必须初始化,且不可重新指向

野指针、空指针、空悬指针

野指针

定义

指向无效内存区域(比如未初始化、已释放或者越界访问)的指针称之为野指针。野指针会导致未定义(UB)行为。

危害:

  • 访问野指针可能引发段错误(Segmentation Fault)
  • 可能破坏关键内存数据,导致程序崩溃。

产生场景:

  1. 指针变量未初始化

    int *p;    // p未初始化,是野指针
    printf("%d\n", *p); // 危险操作:p就是野指针
    
  2. 指针指向已释放的内存

    int *p = malloc(sizeof(int)); // 在堆区申请1个int大小的内存空间,将该空间地址赋值给指针变量p
    free(p); // 释放指针p指向的空间内存
    printf("%d\n", *p); // 危险操作:p就是野指针
    
  3. 返回局部变量的地址

    int* fun(int a, int b)
    {int sum = a + b; // sum就是一个局部变量return &sum;  // 将局部变量的地址返回给主调函数
    }int main()
    {int *p = fun(2,3);printf("%d\n", *p); // 危险操作:p就是野指针
    }
    

如何避免野指针:

  1. 初始化指针为NULL

  2. 释放内存后立即置指针为NULL

  3. 避免返回局部变量的地址

  4. 使用前检查指针有效性(非空校验,边界检查)。

    int fun(int *pt)
    {int *p = pt;// 校验指针if(p == NULL) // 结果为假   等价于 if(!p)  其实底层: if(p == 0){printf("错误!");return -1;}printf("%d\n", *p);return 0;
    }
    

空指针

**定义:**值为NULL的指针,指向地址0x000000000000(系统保留,不可访问)

**作用:**明确表示指针当前不指向有效内存,一般用作指针的初始化。

示例:

int *p = NULL;  // 初始化为空指针free(p);   // 释放后置空
p = NULL;

空悬指针

**定义:**指针指向的内存已经被释放,但未重新赋值。空悬指针是野指针的一种特例。

示例:

char *p = malloc(100); // 在堆区分配100个char的空间给p
free(p); // 释放指针p指向的内存空间
printf("%p,%d\n", p, *p); // p可以正常输出,*p此时属于危险操作 
// p指向的内存空间被回收,但是p指向空间的地址依然保留,此时这个指针被称作空悬指针

void与void*的区别

定义

  • void: 表示“无类型/空类型”,用于函数返回类型或者参数。

    void func(void);    // 没有返回值也没有参数,一般简写:void func();
    
  • *void:**通用指针类型(万能指针),可指向任意类型数据,但需要强制类型转换后才能解引用。

    void* ptr = malloc(4);  // ptr指向4个字节大小的堆内存空间  
    // 存放int类型数据
    int *p = (int*)ptr;
    *p = 10;// 存放float类型数据
    float* p1 = (float*)ptr;
    *p = 12.5f;// 存放char类型数组
    char* p2 = (char*)ptr;// 以下写法完全错误
    float* ptr = malloc(4);
    int *p = (int*)ptr;  // 此时编译报错,类型不兼容 float* int*
    

    注意:只能是具体的类型(int*,double*,float*,char*...)和void*之间转换

注意事项

  • void不能直接解引用(*ptr 会报错
  • 函数返回void*需要外部接收的时候明确类型(不明确类型,就无法解引用)

示例

#include <stdio.h>/*** 定义一个返回类型为void*类型的指针函数*/
void* proces_data(void* p)
{return p;
}int main(int argc, char *argv[])
{// int类型int m = 10;int* p_int = &m;int* result_int = (int*)proces_data(p_int);printf("Integer value:%d\n", *result_int);// double类型double pi = 3.1415926;double* p_double = &pi;double* result_double = (double*)proces_data(p_double);printf("Double value:%lf\n", *result_double);// void* p_void = proces_data(p_double);// printf("Void value:%lf\n", *p_void);// *p_void = 20;// 注意:void* 修饰的指针是可以进行赋值操作的,但是不能对其解引用return 0;
}

运行结果:

在这里插入图片描述

http://www.dtcms.com/a/308075.html

相关文章:

  • Kubernetes 应用部署实战:为什么需要 Kubernetes?
  • Apache Tomcat样例目录session操纵漏洞解读
  • Import Maps 实战指南:无需打包器,浏览器原生模块路径重映射!
  • python 检查带有标题行,以逗号为分隔符的文本文件
  • Vue 的双向数据绑定原理
  • 自我学习----绘制Mark点
  • 解决Pycharm内存一直升高卡死、反应慢、CPU占用高
  • 《通信原理》学习笔记——第六章
  • IntelliJ IDEA 的常用快捷键
  • Git 详细安装配置教程(Windows版)
  • 以微服务为基础搭建一套脚手架开始前的介绍
  • BGP高级特性之认证
  • python刷题关键记录【常用api使用方法总结,常用函数使用方法】
  • RHEL 8.10 离线安装 Ansible 完整教程
  • 网络基础——路由控制
  • iOS 类存储 与 C# 类存储 的差异
  • 正则化都是放在模型的哪个位置呢?
  • 系统讲解图片格式转换:为什么要转换、怎么转换
  • 数据治理:数字化时代的 “治” 与 “理” 之道 —— 破解企业数据资产困局
  • 【2025/07/31】GitHub 今日热门项目
  • 代码随想录day51图论2
  • Spring MVC体系结构和处理请求控制器
  • 图论:SPFA算法
  • 嵌入式操作系统快速入门(1):快速入门操作系统常见基础概念
  • CMake项目中如何按目录结构分离显示Header和Source文件
  • LPC2132GPIO
  • Ubuntu 内网多台服务器时间同步方案(适用于临时能上外网的环境)
  • 电商作图:解锁“素材裂变”和“产品测款”新姿势
  • Zombie Process
  • Apache Camel 简介