)
目录前言一、指针是什么指针类型二、定义指针变量语法取地址与解引用三、指针的核心用途被调修改主调四、指针与一维数组指针运算通过指针访问数组元素五、指针与字符串两种表示方式const 修饰指针用指针实现字符串函数memcpy按字节拷贝六、指针与二维数组七、指针数组与多级指针指针数组二级指针八、常见注意事项九、总结前言指针是 C 语言中最重要的概念之一也是 C 语言区别于其他高级语言的重要特色。正确而灵活地运用指针可以有效地表示复杂的数据结构动态分配内存方便地使用字符串高效地操作数组在函数调用中获得多个返回值直接处理内存地址可以说不掌握指针就没有掌握 C 语言的精华。但指针也是初学者最容易感到困难的部分原因有两个指针本身比较抽象指针几乎可以和前面学过的所有内容关联起来——普通变量、数组、函数、甚至指针本身。一、指针是什么计算机的内存是按字节编址的每个字节都有一个唯一的编号这个编号就是地址。指针就是地址地址就是内存单元的编号。地址本身是一个数值但它不是普通数值而是“具有指向含义的数值”。指针类型C 语言为“地址”这种特殊数据设计了一种数据类型——指针类型。int a 10; // a 是 int 类型 int *p; // p 是指针类型专门用来存放地址int类型用来处理整数指针类型用来处理地址。二、定义指针变量语法基类型 *指针变量名;例如int *p; // 指向 int 型数据的指针 char *q; // 指向 char 型数据的指针 float *r; // 指向 float 型数据的指针说明基类型表示指针所指向的空间中存放的数据类型。*定义时只是用来说明该变量是指针类型。指针变量名标识符命名规则同普通变量。指针变量本身也占内存空间32 位系统地址是 32 位占 4 个字节64 位系统地址是 64 位占 8 个字节。取地址与解引用int a 10; int *p a; // a 取出 a 的地址赋给 p取地址运算符。*解引用运算符指针运算符取出指针所指向空间中的值。printf(%d\n, *p); // 输出 10 *p 20; // 通过指针修改 a 的值 printf(%d\n, a); // 输出 20解引用*p的运算过程取出p中存放的地址到内存中定位从定位处开始偏移出sizeof(基类型)大小的一块空间将这块空间当做一个基类型变量来看待。三、指针的核心用途被调修改主调函数参数传递默认是值传递形参的改变不会影响实参。如果希望在函数内部修改主调函数中的变量就需要传递地址。void addOne(int *p) { *p *p 1; // 通过指针修改主调中的变量 } int main() { int n 10; addOne(n); // 传递 n 的地址 printf(%d\n, n); // 输出 11 return 0; }要修改谁就传谁的地址在被调函数中一定要有相应的*p运算。四、指针与一维数组数组名代表数组首元素的地址也是整个数组的起始地址。int a[5] {1, 2, 3, 4, 5}; int *p a; // 等价于 int *p a[0];指针运算p 1地址增加一个基类型大小这里是 4 字节p N地址增加 N 个基类型大小p - 1、p - N地址减少p、p--指针前后移动p - q两个同类型指针相减得到它们之间相差的元素个数关系运算 !比较地址编号。通过指针访问数组元素a[i] *(p i) *(a i) p[i]示例for (int i 0; i 5; i) { printf(%d , *(p i)); //等价于a[i] }五、指针与字符串字符串在内存中按字符数组方式存储以\0结束。两种表示方式char s1[] hello; // 栈上的字符数组可读可写 char *s2 hello; // 指向字符串常量区只读s1可以修改s1[0] H;s2不能修改s2[0] H;会导致段错误。const 修饰指针const char *p; // 指向的内容只读*p (值)不能改 char const *p; // 同上 char * const p; // 指针本身只读p (地址)不能改 const char * const p; // 两者都只读const就近原则离谁近就限定谁。用指针实现字符串函数char *Strcpy(char *dest,const char *src){ char *ret dest; while(*src!\0){ *dest *src; src; dest; } *dest \0; return ret; } char *Strncpy(char *dest,const char *src,size_t n){ char *ret dest; while(n0){ if(*src ! \0){ *dest *src; dest; src; }else { *dest \0; dest; } --n; } return ret; } char *Strcat(char *dest,const char *src){ char *ret dest; while(*dest!\0) dest; while(*src!\0){ *dest *src; src; dest; } *dest \0; return ret; } char *Strncat(char *dest,const char *src,size_t n){ char *ret dest; while(*dest!\0) dest; while(n0 *src ! \0){ *dest *src; src; dest; --n; } return ret; } int Strcmp(const char *s1,const char *s2){ while(*s1*s2 *s1!0 *s2!0){ s1; s2; } return *s1-*s2; } int Strncmp(const char *s1,const char *s2,size_t n){ int ret; while(n0 *s1*s2 *s1!0 *s2!0){ s1; s2; --n; } if(n) ret *s1-*s2; else ret *(--s1) - *(--s2); return ret; }memcpy按字节拷贝void *Memcpy(void *dest,const void *src,size_t n){ char *d dest; const char *s src; while(n){ *d *s; d; s; --n; } }void *是万能指针可以接受任意类型的地址不能直接对void *解引用需要强制类型转换。六、指针与二维数组二维数组的本质是“一维数组的数组”。int a[3][4] {1,2,3,4,5,6,7,8,9,10,11,12};a是数组名类型是int(*)[4]行指针a i指向第i行*(a i)是第i行的首地址类型是int **(*(a i) j)等价于a[i][j]。定义行指针int (*p)[4] a; // p 指向一个有4个int元素的数组示例void PrintArrayP(int *begin,int *end){ while(beginend){ printf(%d ,*begin); } putchar(\n); } int main{ int a[10] {1,2,3,4,5,6,7,8,9,10}; int *p a; //int *p; p a; PrintArrayP(a,a9);//数组名就是首元素地址 }七、指针数组与多级指针指针数组char *s[5]; // 包含5个 char* 元素的数组用于存放多个字符串char *names[5] {hello, world, china, english, america};二级指针int a 10; int *p a; int **pp p; // pp 指向 p是二级指针*pp得到p**pp得到a。八、常见注意事项野指针指针变量未初始化指向不确定。空指针int *p NULL;表示明确不指向任何有效空间。不要解引用未初始化的指针。*p等价于p*a等价于a。指针类型要匹配不同类型的指针赋值需要强制转换。void *不能直接解引用必须先转换类型。九、总结指针就是地址指针变量是专门用来存放地址的变量。定义形式基类型 *变量名。取地址*解引用。指针的核心用途被调修改主调。指针与数组关系密切a[i] *(a i) *(p i) p[i]。指针可以操作字符串实现strlen、strcpy、strcat、strcmp等。二维数组用行指针int (*p)[列数]操作。指针数组和二级指针用于处理多个字符串或多级间接访问。注意野指针、空指针、const 修饰和类型匹配。