本文记录了一些C指针的定义,包括空指针、野指针、数组指针、指针数组、函数指针、指针函数、多级指针等等一些知识点的小结。
参考工程(vs19编写):代码下载
1.空指针:
没有赋值的指针变量(没有指向内存变量的地址),对空指针操作会造成程序的Core dump(段错误)
#include <stdio.h>
#include <stdlib.h>
int main()
{
int* p_i = 0;
printf("p_i = %p \r\n", p_i);
*p_i = 2;// 试图对空指针进行赋值操作,程序会崩溃
return 0;
}
2.野指针:
指向的内存被释放了的指针,但指针的值不会清零。对其操作不可预知。
以下操作均为错误操作。
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *p_i = (int *) malloc(sizeof(int));
*p_i = 10;
printf("p_i = %d \r\n",*p_i);
free(p_i);
*p_i = 2;
printf("pi = %d \r\n",*p_i);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *p_i = (int *) malloc(sizeof(int));
*p_i = 10;
printf("p_i = %d \r\n",*p_i);
free(p_i);
*p_i = 2;
printf("pi = %d \r\n",*p_i);
FILE *fp=fopen("1.txt","w");
printf("fp is %p\r\n",fp);
fclose(fp);
printf("fp is %p\r\n",fp);
//fclose(fp);
return 0;
}
free ( p );带上p = 0;避免野指针错误。
#include <stdio.h>
#include <stdlib.h>
int main()
{
char* p = (char*)malloc(sizeof(char)*4);
free(p);
p = 0;
strcpy(p,"123");
printf("%s\r\n", p);
return 0;
}
3.数组
数组名、数组地址、数字首元素地址均为同一地址。
#include <stdio.h>
#include <stdlib.h>
int main()
{
char num[20] = "hello word";
printf("数组名 %p \r\n", num);
printf("数组地址 %p \r\n", &num);
printf("数组首元素 %p \r\n", &num[0]);
printf("打印以数组名 %s \r\n", num);
printf("打印以数组地址 %s \r\n", &num);
printf("打印以数组首元素 %s \r\n", &num[0]);
return 0;
}
结果:
sizeof的坑
sizeof(数组)会得到数组长度,但是通过函数传入数组的指针sizeof却是指针的大小。
如果数组的长度小于指针的大小,会导致程序崩溃,如下代码。不同系统指针大小不一致。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void fun(char* p);
int main()
{
char str[3] ;
printf("%d\r\n", sizeof(str));
fun(str);
return 0;
}
void fun(char* p)
{
printf("%d\r\n", sizeof(p));
memset(p, 0, sizeof(p));
scanf("%s",p);
printf("%%p :%p \r\n",p);
printf("%%s :%s \r\n",p);
}
建议:不要在函数里面调用memset时,使用sizeof计算长度。
补充:结构体用sizeof计算指针也会出错。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Student
{
int age;
char name[21];
};
void fun(struct Student* p)
{
printf("in fun() sizeof: %ld\r\n", sizeof(p));// sizeof(struct Student)
memset(p, 0, sizeof(p));// 不建议使用,指清除了指针大小个字节
// 建议改为下述代码,使用sizeof(struct Student)
// memset(p, 0, sizeof(struct Student));
}
int main()
{
struct Student stu = { 22,"LiHua" };
printf("in main() sizeof :%ld\r\n", sizeof(stu));
fun(&stu);
return 0;
}
vs19编译结果:(指针4字节)
虚拟机结果:(指针8字节)
为什么结构体大小28?
因为int 4字节,结构体4字节对齐,所以为28。
4.地址的运算
地址可加减运算:+1表示加一个存储单位的地址,-1减一个存储单位的地址。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char cc[4];
int ii[4];
double dd[4];
printf(" char: %p %p %p %p\r\n", cc, cc+1, cc+2, cc+3);
printf(" int: %p %p %p %p\r\n", ii, ii+1, ii+2, ii+3);
printf("double: %p %p %p %p\r\n", dd, dd+1, dd+2, dd+3);
return 0;
}
vs19运行结果:(char + 1,int + 4,double + 8)
同样的使用地址操作可以直接访问数组元素,如*(cc+1)
与cc[1]
等效。
众所周知,C语言的标准库没有字符串截取函数。为什么?笔者认为可能是没有必要。因为可以直接地址运算操作地址实现。
字符串截取举例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char src[12] = "hello world";
char dest[10];
memset(dest, 0, 10);
// 将src截取后面world赋值给dest
strncpy(dest,src + 6, 5);// 地址操作
printf("dest:%s\r\n",dest);
return 0;
}
vs19运行结果:
5.字符串常量的地址:
在C里面字符串实际就是以‘\0’为结束符的char类型一维数组,故同样可以作为一个地址。它的值为字符串的首地址。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define HD "hello world"
int main()
{
char *str = "hello world";
char* str1 = "23333";
printf("HD:%s \r\n", str + 6);
printf("str1:%s\r\n",str1);
return 0;
}
结果:
但因为是常量,所有并不能直接通过str[0] = ‘H’来改变字符串的值,使字符串变成“Hello word”。这种做法是错误的。
6.函数指针
定义:C程序每一个函数都有一个入口地址,所谓函数指针就是指向函数入口地址的指针变量。
应用:调用函数和做函数的参数。
定义格式:
返回值类型 (* 函数指针)(参数列表...)
返回值和参数列表应该和要指向的函数保持一致。
此外:函数名也是指针,即fun_max
和&fun_max
等效。
改变指针变量的指向不同的函数,可以实现类似多态的效果。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 返回最大值
int fun_max(int x, int y)
{
if (x > y) return x;
return y;
}
// 返回最小值
int fun_min(int x, int y)
{
if (x < y) return x;
return y;
}
int main()
{
int a = 2;
int b = 5;
// 定义函数指针
int (*fun_ptr)(int, int);// 格式要匹配,返回值、参数及参数个数顺序都要一致。
fun_ptr = &fun_max; // 函数名也是指针,与fun_ptr = fun_max;等效
printf("max:%d\r\n", fun_ptr(a,b));
fun_ptr = fun_min;
printf("min:%d\r\n", fun_ptr(a, b));
return 0;
}
运行结果:
max:5
min:2
函数指针常常和typedef组合使用,更直观。
如上可以写为:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 格式要匹配,返回值、参数及参数个数顺序都要一致。
typedef int (* _fun_ptr_t)(int,int);
// 返回最大值
int fun_max(int x, int y)
{
if (x > y) return x;
return y;
}
// 返回最小值
int fun_min(int x, int y)
{
if (x < y) return x;
return y;
}
int main()
{
int a = 2;
int b = 5;
// 使用typedef定义函数指针
_fun_ptr_t fun_ptr;
fun_ptr = &fun_max; // 函数名也是指针,与fun_ptr = fun_max;等效
printf("max:%d\r\n", fun_ptr(a,b));
fun_ptr = fun_min;
printf("min:%d\r\n", fun_ptr(a, b));
return 0;
}
7.指针函数:
定义:返回值为一个地址的函数
#include<stdio.h>
char* getword(char);
char* getword(char c)
{
switch (c)
{
case'A':return"Apple";
case'B':return"Banana";
case'C':return"Cat";
case'D':return"Dog";
default:return"None";
}
}
int main()
{
char input;
printf("请输入一个大写字母:\n");
scanf("%c", &input);
printf("%s\n", getword(input));
}
测试输入字母输出对应字符串
比如:
请输入一个大写字母:
C
Cat
8.数组指针与指针数组
指针数组:
int *p1[10];
解释:[]的优先级比*高,p[10]表示一个大小为10个单位的数组,int *再来修饰数组的内容,表示p1数组为一个存储10个int型指针的数组。
数组指针:指向一个数组的指针。
int (*p2)[10];
解释:(*p2)表示一个指针,[10]说明是一个数组,int修饰数组元素的类型,综上就是定义了一个指针p2,它指向有10个int元素数组的首地址。
无聊。。。。
9.多级指针
无聊的一个概念,说白了就是指针指向指针。
举例代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int i = 2;
int* pi = &i;
int** ppi = π
int*** pppi = &ppi;
printf("%d\r\n", i);
printf("%d\r\n", *pi);
printf("%d\r\n", **ppi);
printf("%d\r\n", ***pppi);
return 0;
}
类似俄罗斯套娃,要是乐意可以一直“取地址”再“解引用”。
运行结果:(说明了这都是2)
2
2
2
2