C语言基础四(指针)
指针
指针表示一个地址,什么类型的指针就应该指向什么类型的内存空间,例如int *类型的指针就应该指向一个int类型的空间。
int a = 7;
int *p = NULL; //指针的定义
p = &a; //指针的绑定
*p = 5; //指针的解引用
指针的运算
对于同类型指针p1,p2,一般有这几种运算
p1+3, p1-3, p2-p1
int a[5] = {1, 2, 3, 4, 5};
int *p1 = a; //p1指向第一个元素
int *p2 = p1 + 3; //p2 指向第4个元素
int c = p2 - p1; //=3,表示相差3个指针所指向的类型的空间。
指针数组和数组指针
int *p[5]; //指针数组,因为[]优先级比价高,所以p表示一个数组,里面存放5个int指针
int (*p)[5]; //数组指针,p是一个指针,指向一个int[5]类型的空间
int **c;
int a1[3] = {1, 2, 3}, a2[3] = {4, 5, 6};
int b[2][3] = {a1, a2};
等价于int b[2][3] = {1, 2, 3, 4, 5, 6};
int (*p)[3] = b; //数组指针可以与二维数组匹配
//*p 表示的是a1这个数组,*(p+1)表示a2这个数组。 **(p+1)表示的是a2中的第一个元素
//这里的b和p在参与运算时,相当于二维指针,但是与 c 的类型是不同的,数组指针p指向int[3],c指向int *。
char (*p)[10];
char arr[][10] = {
"hello",
"world",
};
//p是一个数组指针,他可以与二维数组arr匹配。
//这里每行的字符数必须确定,行数可以不用注明,
//因为在内存中存放时都是以一维数组的形式按顺序排放的,每行字符数的增减直接影响到下一行的位置,
//所以要想存储下一行,必须先确定了上一行的存储大小。
char arr1[][5][10];//三维数组也必须要确定第二维和第三维的大小,可以与char (*q)[5][10]匹配;
指针的强制类型转换
对指针进行强制类型转换一般是需要 目标指针类型的偏移量 ,或者需要 等号两边的指针类型 相互匹配。
char arr[] = "abcdefghijk";
int *p = (int *)arr + 1;
printf("%c %c %c %c\n", *(char *)p, *((char *)p+1), *((char *)p+2), *((char *)p+3));
//输出为e f g h
#define ADDR 0
int *p = (int *)ADDR;
嵌入式是一种寄存器和内存统一编址的硬件设备,对寄存器读写操作时,可以直接对其地址进行操作。
假设地址是一个合法地址0x7FFF1234,这个地址的寄存器是32位寄存器,对这个寄存器读写操作
*(int *)0x7FFF1234 = 1;
或者将其写成宏定义
#define rG *((volatile unsigned int *)0x7FFF1234)
int a = rG;
rG = 1;
//volatile 关键字,表示经常改变的,编译器不会做优化,每次都是从内存中重新读取数据。
函数指针
函数名就是函数的首地址
int * f(int a, int b); //这是一个返回值为int *类型的函数;
int (*f1)(int a); //函数指针,f1是一个指针,指向类型是int (*)(int )
int max(int a, int b){
return a > b ? a : b;
}
int main()
{
int (*f)(int, int); //定义了一个函数指针,指向‘返回值类型为int,有两个参数类型都是int’的函数
f = max; //函数指针f指向函数max,或者写成f = &max;
int c = f(1, 2); //或者写成c = (*f)(1, 2);
printf("%d \n", c);
typedef
typedef int * p_int1;
#define p_int2 int *
p_int1 a, b; //相当于int *a, *b;
p_int2 c, d; //相当于int *a, b;
//定义一个函数类型,
理论上应该是typedef (void (*)(int , int )) FUNC1;
但编译器编译不通过的,实际上应该写成
typedef int (*FUNC2)(int , int );
FUNC2是新类型名,代表了原类型int (*)(int , int )
typedef可以简化类型
typedef int (*FUNC)(int , int );
int add(int a, int b){return a + b;}
int sub(int a, int b){return a - b;}
//定义一个返回值为FUNC类型的函数,返回值类型为int (*)(int , int )
//int (*calculate(char operate))(int , int )
//通过typedef可以简化成下面的定义。
FUNC calculate(char operate)
{
switch(operate)
{
case '+':
return add;
case '-':
return sub;
dafault:
return NULL;
}
}
//最后还需定义一个用户调用的函数
int cal(int a, int b, char operate)
{
FUNC fp = calculate(operate); //fp现在指向了加或者减的那个函数,或者指向NULL。
if(fp)
return fp(a, b);
else
return -1;
}
malloc和calloc
申请的是堆内存。堆内存不会自己释放,直到整个进程运行完毕。
所以malloc( )申请的空间一定要保存好这段空间的首地址,用完之后free( )释放.
函数原型
void *malloc(unsigned int num_bytes);//申请成功,返回这段空间的首地址,不成功返回NULL
地址为NULL的空间在内核区域,所以在申请时,一定要判断是否申请成功
当申请的字节数为0时,也会申请出一个最小单位的空间,这个空间用来保存申请空间的大小。也可以用free()释放。
int *p = (int *)malloc(sizeof(int) * 5); //在堆内存申请5个int空间
if(NULL == p){
printf("malloc error\n");
return 0;
}
memset(p, 0, sizeof(int) * 5); //给这段空间清零。
//用完这段空间之后
free(p); //指针p在使用时不能移动,否则释放不彻底,会内存泄漏
p = NULL;//防止野指针乱指。
NULL
NULL是一种地址类型的0,(void *)0.
表示内存中最低端,也是就系统内核最开始的地方。
const
const int a = 1; //a在C和C++中存储的方式有所不同。
对于全局变量或者静态变量,C和C++中都是存储在数据段,也就是全局/静态存储区;
对于局部变量
C语言中,和普通局部变量一样也是存储在栈区,
C++中对const进行了优化,如果初始化为常量表达式,将其视为标识符常量,和宏定义类似,编译时直接替换,这叫常量折叠。
宏定义是在预编译阶段进行字符替换,替换结束后,就会丢弃原字符。
如果通过 别的变量的值 或者 地址中的值 初始化,就是不可折叠常量。
<常量折叠>(constant folding),编译的时候,任何用到 “a” 的地方,都会直接用常量表达式的值 “1” 去替换。
这时候a的值不需要访问存储空间来确定,程序中任何出现 a 的地方在编译期间就已经被替换,即使通过取地址来修改这个值,也是相当于改变了一个副本而已,取地址时,编译器会为a另外分配空间。
C++中const定义的变量是可以用于定义数组的元素个数。而C语言就没有这个优化特性,所以C语言的const修饰的变量是不能用来定义数组元素个数的。
const修饰指针
const int *a; //*a(a指向的空间)是const的
int const *a; //同上
int * const a; //地址a是const的
int **const*a; //*a(a指向的空间,类型是int **)是const的,
int *const**a; //**a(a指向的空间内容所指向的空间,类型是int *)是const的,
可以理解为,const修饰它之后的内容,可以将他之后的内容看作是指针运算的表达式(表示修饰的空间)
上一篇: c语言之指针(四)
下一篇: Django数据库查询优化(二)