接着上一篇初探C课程系列一 本章继续说 指针、函数、预处理器。
指针
指针是一个特殊的变量,它里面存储的数值被解释成为内存里的一个地址。要搞清一个指针需要搞清指针的四方面的内容:指针的类型、指针所指向的类型、指针的值或者叫指针所指向的内存区、指针本身所占据的内存区。
总结:指针是一个变量,其值为地址。
1.指针的类型
从语法的角度看,你只要把指针声明语句里的指针名字去掉,剩下的部分就是这个指针的类型。这是指针本身所具有的类型。让我们看看例一中各个指针的类型:
(1)int*ptr;//指针的类型是int*
(2)char*ptr;//指针的类型是char*
(3)int**ptr;//指针的类型是int**
(4)int(*ptr)[3];//指针的类型是int(*)[3]
(5)int*(*ptr)[4];//指针的类型是int*(*)[4]
当你通过指针来访问指针所指向的内存区时,指针所指向的类型决定了编译器将把那片内存区里的内容当做什么来看待。
从语法上看,你只须把指针声明语句中的指针名字和名字左边的指针声明符*去掉,剩下的就是指针所指向的类型。例如:
(1)int*ptr; //指针所指向的类型是int
(2)char*ptr; //指针所指向的的类型是char
(3)int**ptr; //指针所指向的的类型是int*
(4)int(*ptr)[3]; //指针所指向的的类型是int()[3]
(5)int*(*ptr)[4]; //指针所指向的的类型是int*()[4]
在指针的算术运算中,指针所指向的类型有很大的作用。
指针的类型(即指针本身的类型)和指针所指向的类型是两个概念。当你对C 越来越熟悉时,你会发现,把与指针搅和在一起的"类型"这个概念分成"指针的类型"和"指针所指向的类型"两个概念,是精通指针的关键点之一。我看了不少书,发现有些写得差的书中,就把指针的这两个概念搅在一起了,所以看起书来前后矛盾,越看越糊涂。
指针的值是指针本身存储的数值,这个值将被编译器当作一个地址,而不是一个一般的数值。在32 位程序里,所有类型的指针的值都是一个32 位整数,因为32 位程序里内存地址全都是32 位长。指针所指向的内存区就是从指针的值所代表的那个内存地址开始,长度为si zeof(指针所指向的类型)的一片内存区。以后,我们说一个指针的值是XX,就相当于说该指针指向了以XX 为首地址的一片内存区域;我们说一个指针指向了某块内存区域,就相当于说该指针的值是这块内存区域的首地址。指针所指向的内存区和指针所指向的类型是两个完全不同的概念。在例一中,指针所指向的类型已经有了,但由于指针还未初始化,所以它所指向的内存区是不存在的,或者说是无意义的。
以后,每遇到一个指针,都应该问问:这个指针的类型是什么?指针指的类型是什么?该指针指向了哪里?(重点注意)
指针本身占了多大的内存?你只要用函数sizeof(指针的类型)测一下就知道了。在32 位平台里,指针本身占据了4 个字节的长度。指针本身占据的内存这个概念在判断一个指针表达式(后面会解释)是否是左值时很有用。
指针的类型、指针所指向的类型、指针的值或者叫指针所指向的内存区、指针本身所占据的内存区来源:https://blog.csdn.net/constantin_/article/details/79575638
注意:声明指针或者不再使用后都要将其置为0 (NULL)
野指针
1.未初始化的指针
2. 指针指向的内存被释放了,而指针本身没有置NULL
总结:野指针,也就是指向不可用内存区域的指针
悬空指针
指针最初指向的内存已经被释放了的一种指针
C指针声明正确书写例子
int *a; 正规
int* a;
int * a;
int* a,b;
其他书写不规范,因为 其他写法看起来有歧义
使用:
//声明一个整型变量
int i = 100; //将i的地址使用取地址符给p指针
int *p = &i;
printf("%#x\n", &i);
printf("%#x\n", &p);
system("pause");
return 0;
%#x:是16进制输出
%#x是带格式输出, 效果为在输出前加0x.如下图所示:
指针多少个字节?
指向地址,存放的是地址,地址在 32位中指针占用4字节 64为8,效果图如下:
解引用
解析并返回内存地址中保存的值。举个例子如下:
//声明一个整型变量
int i = 100;
int *p = &i;
printf("%d\n", *p);//4
system("pause");
return 0;
p指向一个内存地址,使用*解出这个地址的值 即为 100。
指针运算
eg1:
int main()
{
int arrays[3][3] = { { 11,22,33 },{ 44,55,66 } ,{ 77,88,99 } };
int(*array_test)[3] = arrays;
printf("取出22:%d\n", *(*(array_test + 0) + 1));
printf("取出55:%d\n", *(*(array_test + 1) + 1));
printf("取出99:%d\n", *(*(array_test + 2) + 2));
system("pause");
return 0;
}
输出结果:
eg2:
int main()
{
int i1[] = { 1,2,3,4,5 };
//& 取地址符
int *p1 = i1;
for (size_t i = 0; i < 5; i++) {
printf("%d\n", *p1++);}
system("pause");
return 0;
}
输出结果:
总结:指针指向地址,指针运算实际上就是移动指针指向的地址位置,移动的位数取决于指针类型(int就是32位)
数组和指针
在c语言中,指针和数组名都表示地址
1、数组是一块内存连续的数据。
2、指针是一个指向内存空间的变量
多级指针
指向指针的指针
一个指针包含一个变量的地址。当我们定义一个指向指针的指针时,第一个指针包含了第二个指针的地址,第二个指针指向包含实际值的位置。
int main()
{
int a = 10; int *i = &a; int **j = &i; // *j 解出 i
printf("%d\n", **j);
system("pause");
return 0;
}
输出结果:10
函数
C中的函数与java没有区别。都是一组一起执行一个任务的语句,也都由 函数头与函数体构成。
函数位置
1.在使用函数之前,必须先声明函数。
函数参数
1.传值调用,2.引用调用。
void change1(int i) {
i = 10;
}
void change2(int* i) {
*i = 10;
}
void change3(int** i) {
int j = 100;
*i = &j;
}
#include <stdarg.h>
int main()
{
//函数形参
//1、传值
int i3 = 1;
change1(i3);
printf("change1 i3=%d\n", i3);
//2、传引用
//不修改指针的值 修改的是指针指向内存的数据
int *p9 = &i3;
//修改i3的值
change2(p9);
system("pause");
return 0;
}
预处理器
预处理器 | 说明 |
#include | 导入头文件 |
#if | if |
#elif | else if |
#else | else |
#endif | 结束 if |
#define | 宏定义 |
#ifdef | 如果定义了宏 |
#ifndef | 如果未定义宏 |
#undef | 取消宏定义 |
定义:预处理器是一个文本替换工具(宏就是文本替换)。宏一般使用大写区分。
宏变量:在代码中使用 A 就会被替换为1,举个例子:
#define A 1
宏函数
#defind test(i) i > 10 ? 1: 0
优点:
文本替换,每个使用到的地方都会替换为宏定义。
不会造成函数调用的开销(开辟栈空间,记录返回地址,将形参压栈,从函数返回还要释放堆
栈。)
缺点:
生成的目标文件大,不会执行代码检查