3.C语言笔记:指针数组、函数
1.指针数组有若干相同类型的指针变量构成的数组。数据类型 * 数组名[大小]指针数组int * p[3];数组指针int (*p)[4] a;int a 10,b 20, c 20; int * p[3]; p[0] a; p[1] b; p[2] c; printf(a-b-c:%d %d %d\n,a,b,c); printf(%d %d %d\n,*p[0], *p[1], *p[2]); printf(%p %p %p\n,a, b, c); printf(%p %p %p\n,p[0],p[1],p[2];p1p是指针数组名1就是移动一格指针的大小8字节64位原理p退化成p[0] 一级指针对第一个元素p[0]取地址1就指向第二个而p做为指针p[0],p[1]p[2]每个元素占8个字节64位为指针数组里面的指针定义指向谁只能一个一个来而数组指针可以int (*p) [3] a ;(这里a是二维数组a[2][3])int a[] {5,8,2,5,9,1}; int * p[5]; int i; for(i 0; i sizeof(a) / sizeof(int); i){ p[i] a[i]; 为每个指针定义指向数组的一个元素 } 或者可以 int *p[5] {a[0], a[1], a[2], a[3], a[4]}; for(i 0; i sizeof(a) / sizeof(int); i){ printf(%d\n,*p[i]); } return 0;指针数组指向字符串常量#includestdio.h int main (){ char * s[] {BeiJing, ShangHai, GuangZhou}; int i; for(i 0; i sizeof(s) / sizeof(char *); i){ printf(%s\n,s[i]); 这里说下%s期望的到一个一级指针地址 } s退化成s[0],这是一个指针指向的是s[0] return 0; 可是s[0]本身就是一个指针指向字符串常量里面的第一个元素 } 这样s就变成二级指针了与%s不符这里再说一下s[i]指向的是什么首先{BeiJing, ShangHai, GuangZhou}这并不是数组这个叫【初始化列表】BeiJing是初始化列表里面的【字符串字面量】。所以s[i]比如s[0]指向的是BeiJing这个字符串字面量的首地址。指针数组与二维数组这里不能int *p ap是数组名不可以成为赋值的左值遇到数组首先就想到元素使用元素int a[2][3] {{3,5,1},{3,6,9}}; int * p[2]; a退化成a[0][0] a是一级指针 p[0] a[0]; //a[0][0]; 这里不能int *p a p是数组名不可以成为赋值的左值 p[1] a[1]; //a[1][0]; printf(%d\n,a[0][1]); 输出5 printf(%d\n,*(p[0] 1)); 输出5 p[0] a[0] a[0][0] 数组名在赋值时会退化 printf(%d\n,p[0][1]); 输出5 int i ,j; for(i 0; i 2; i){ for(j 0;j 3;j){ //printf(%d,a[i][j]); printf(%d %d,*(p[i] j),p[i][j]);数组指针和指针数组int * p[5]指针数组是一维数组每个元素都是int *指针数组经常结合二维数组使用存储每行首元素的地址本质是数组存储内容是是指针而已p是数组名数组名不能被赋值。int (*p)[5]指向数组的指针本质是指针指向的内容是有5个元素的一维数组一维数组名取地址或者二维数组名都可以赋给它。int a[2][3] {{3,5,1},{3,6,9}}; int (*p)[3] a ; int i ,j; for(i 0; i 2; i){ for(j 0;j 3;j){ printf(%d,*(*(p i) j)); int b[3] {1,5,9}; int *q b; for(i 0; i 3; i){ printf(%d,*(q i)) // q是指向数组首地址的指针q是b[0] i *(b[0] i) 等于b[i] int b[3] {1,5,9}: int *p[3]; p b; for(i 0; i 3; i){ printf(%d,*(*p i)); p是b 。 *p是b b[0] 所以 *p i 是指向b[i] **p i是b[i]我的理解int *p[3] :这是指针数组p是数组名默认指向p[0]这个指针数组p的第一个元素每个p[0]都是int*类型实际上p是int**类型。p[0]需要手动赋值如p[0] a[0](a是二维数组) 或p[0] a[0] a是一维数组p[0]始终是int*一、这时要区分如果a是二维数组p[0] a[0]注意这里不是p[0]指向a[0]这里是赋值指向应该是p[0] a[0],这就变成p[0]等于a了类型不符合。二维数组名a,是数组指针 类型是int(*)[4]这时p[0]是指向a[0][0] 也就是p[0] a[0][0] 。p[0]是一级指针二、如果a是一维数组p[0] a 才对尽量写成对元素取地址的形式 p[0] a[0]写法 p[0] a[0]; p[1] a[1]a是个一维数组名也是一级指针退化成a[0]。p[0] a[0],p[0]是数组p的元素是一个指针一级指针指向a[0]。*p[0] i等于p[0][i] *p[i]如果是int (*p)[3]; a是a[2][3],p是指向a[0]的指针 a[0]是数组表达式a[0]可退化成a[0][0]int (*p)[3] a; 这时 p不是数组p只是一个指针 p a a[0]p 1 是指向a[1]的指针 *p 1是a[1] 是 a[1][0]所以 *(*(p1)) 等于a[1][0],*(*(pi)j)等于p[i][j] 等于a[i][j]二维数组a[2][3]。a退化成a[0]也就是指向一维数组的指针也叫行指针。这个指针是一级指针。int (*)[3] 是一级数组指针。所以int *p a 可以。a退化成数组指针a[0]2.多级指针指向指针变量的指针变量叫多级指针。一级指针指向处理数据的指针变量。二级指针指向一级指针的指针变量。二级指针 数据类型 ** 指针名#include stdio.h int main (){ int a 10 ; int * p a; int **q p; printf(%d %p %p, a, p, q); printf(%p %p %p, a, p, q); printf(%ld %ld %ld, sizeof(a), sizeof(p), sizeof(q)); printf(%d%d%d, a , *p , **q); 输出101010 printf(%p %P\n, p, p 1); 0x724;0x728 printf(%p %p\n, q, q 1); 0x728;0x730 0x728是指针p自己占的地址 return 0; }多级指针运算指针变量1是 向指针变大的方向移动一个目标数据。多级指针运算也是以其目标变量为单位进行偏移。多级指针运算int **p; p 1移动一个int *变量所占的内存空间 8字节int ***p;p 1移动一格int **变量所占的内存空间 8字节#include stdio.h int main (){ int a[5] {4,6,9,10,20}; int *p[5]; int n sizeof(a) / sizeof(int); int i; int **q; for(i 0; i n; i){ 为指针数组里面的指针定义指向只能一个一个来可以循环 p[i] a[i]; } for(i 0; i n; i){ printf(%d %d\n,a[i], *p[i]); } q p; printf(%d %d %d %d , a[1], *p[1], **(q 1), *q[1]); 输出6666 return 0; } p是指针数组p 退化成 p[0] 。p[0]是指向a[0]的指针。p[0]不是数组名p[0]不会退化。 p[0]是int *类型所以p是int **类型。char *s[5] {apple, pear, peach, orange, banana}; char ** p s; //s[0] printf(%s\n,s[1]); pear printf(%s\n,*(p 1)); pear p; printf(%s\n, *p); pear return 0; }void指针及用法万能指针泛指针void * 没有类型只指向地址。普通指针p 1 void指针不可以int a 10; void *p; p a; printf(%d\n,*(int *)p);#include stdio.h int main() { int a[3] {10, 20, 30}; void *p; p a; printf(%d, *((int *)p 1)); // 输出 20 return 0; }一维索引法平铺 #include stdio.h int main() { int a[2][3] {{1, 2, 3}, {4, 5, 6}}; void *p a; for (int i 0; i 2; i) { for (int j 0; j 3; j) { printf(%d , *((int *)p i * 3 j)); p原来指向a[0] 转变后指向a[0][0] 其实p指向的地址一直没变改变的是指针类型 } printf(\n); } return 0; }void 的指针强制转换时会失去步长步长按新类型算。#include stdio.h int main() { int a[2][3] {{1, 2, 3}, {4, 5, 6}}; void *p a; for (int i 0; i 2; i) { for (int j 0; j 3; j) { printf(%d , *(*((int(*)[3])p i) j)); int(*)[] 是数组指针类型 } printf(\n); } return 0; }const变量const指针常量化变量使变量不能修改。变量被const修饰时用指针访问变量就近原则const修饰谁谁不能改变const修饰的变量必须初始化只定义是不行的const修饰的全局变量是只读段。const修饰的局部变量放栈区。const int m 10; //等价于 int const m 10; //error: m 这时候m就不能修改了 int *p m; (*p); 可以用指针间接访问这时候不报错m变成了11。常量化指针目标限制通过指针改变目标数值但是指针本身存储的地址可以修改。限制内容不能用指针来修改变量。可以改变指针存的地址改完还是不能通过指针修改int m 10; int n 20; const int *p m; 这里可以直接m;只要不用指针*p来改就行 //(*p);报错 *p指针只读不能修改 p n; 没问题 printf(%d,*p); 输出20常量化指针变量指针存的地址不能修改但可以*p 来改变指针指向的变量的值int m 10; int n 20; int * const p m; //p n; 报错 (*p); 可以 printf(%d,*p); 输出11常量化指针及目标不能改指针存的地址也不能改指针所指变量的值。int m 10; int n 20; const int * const p m; //p n; 报错 //(*p); 报错3.main函数的参数argc:命令行参数的个数argv:保存命令行参数的字符串指针其中第0个参数是程序的全名以后的参数为命令行后面跟着的用户输入的参数。argv参数是字符串指针数组其各元素值为命令行中各字符串的首字母。指针数组的长度即为参数个数argc。#includestdio.h int main (int argc, const char * argv[ ]){ if(argc ! 3){ printf(usage: %s ip port\n,argv[0]); return 0; } int i; printf(argc %d\n,argc); for(i 0 ; i argc; i){ printf(%s\n,argv[i]); } return 0; }4.函数函数是一个完成特定功能的代码模块其程序代码独立通常要有返回值返回值可以是空值。数据类型 函数名 形式参数说明{内容return 表达式要与数据类型呼应}如果没有类型写void 如 void 函数名 形式参数说明用逗号 , 分隔多个变量的说明形式。{ }大括号里的叫函数体。函数的目的是模块化代码维护方便。函数的调用函数的使用函数名实际参数函数调用可以作为一个参数放在表达式里来运算。实际参数 函数调用时调用函数传递给被调用函数的数据。编写函数比较两个数谁最大 #include stdio.h int get_max(int a, int b){ if(a b){ return b; }else{ return a; } } int main (){ int i,j; int ret; printf(); scanf(%d%d,i, j); ret get_max(i, j); printf(max: %d\n,ret); return 0; }函数的声明编译器从上往下编译所以声明函数要放到main函数前面。同时形参里变量的名称可以省略但是变量的类型不能省略。int get_max(int a, int b){int get_max(int , int ){ 两种写法都对#includestdio.h void print_hello(); 将声明的名称放main前面编译时就不会报错。 int main(){ print_hello(); return 0; } void print_hello(){ printf(hello world\n); 可以把详细的函数声明写后面。 }编写函数比较两个数谁最大 #include stdio.h int get_max(int , int ){ 这里可以不写形参里的变量名但类型必须写 int main (){ int i,j; int ret; printf(); scanf(%d%d,i, j); ret get_max(i, j); printf(max: %d\n,ret); return 0; } int get_max(int a, int b){ 这里必须写全 if(a b){ return b; }else{ return a; } }应用求幂 #includestdio.h double fun_pow(double x,int n); int main(){ double x 2; int n 5; double ret; 这里可以省略成printf(%lf,fun_pow(x,n)); ret fun_pow(x,n); printf(%lf\n,ret); return 0; } double fun_pow(double x,int n){ int i; double ret 1; for(i 0; i n; i){ ret * x; return ret; 这里是整个函数运行结束后发出的数据 } }函数的传参-全局变量形参定义函数时用的参数目的是接收调用该函数时收到的参数。实参主函数调用时真正传给被调用函数的参数。参数传递程序运行时将实际参数传递给被调用函数的形式参数的过程。传递方式有三种全局变量、值传递、指针传递。全局变量全局变量是在所有函数体外说明的变量。不建议使用。所有函数都可以调用和修改它。值传递调用函数将实参传递给被调用函数被调用函数先创建新的形参新地址然后把实参的值赋给它。原地址的内容不修改交换数值值传递是创建新形参新地址 #includestdio.h void fun_swap(int a, int b); int main (){ int x 200, y 100; printf(before:x%d y%d %p %p\n, x, y, x, y); 原数值原地址 fun_swap(x,y); 交换后数值新地址 printf(after:x%d y%d\n,x,y); 原数值新地址 return 0; } void fun_swap(int a, int b){ int t a; a b; b t; printf(a%d b%d %p %p\n,a , b, a, b); }值传递应用判断素数#includestdio.h int fun_prime(int n); int main(){ int n 23; if(fun_prime(n)){ printf(%d is prime\n,n); }else{ printf(%d is not prime\n,n); } return 0; } int fun_prime(int n){ int i; if(n 1){ return 0; } for(i 2; i n; i){ if(n % i 0){ return 0; } } return 1; }输入一个数看看他以下有多少素数#includestdio.h int fun_prime(int n); int fun_prime_sta(int n); int main(){ int n; printf(input ); scanf(%d,n); int count; count fun_prime_sta(n); printf(%d\n,count); return 0; } int fun_prime(int n){ int i; if(n 1){ return 0; } for(i 2; i n; i){ if(n % i 0){ return 0; } } return 1; } int fun_prime_sta(int n){ int i; int sum 0; for(i 1; i n; i){ if(fun_prime(i)){ printf(%d\n,i); sum; } } return sum; }指针传递按地址传递实参为变量的地址形参为同类型的指针。被调用函数对形参的操作可以直接改变实参的值。#includestdio.h void fun_swap(int * p,int *q); int main(){ int m 10,n 20; fun_swap(m, n); printf(%d %d,m ,n); 实参是地址被指针操作修改了变量的原值 return 0; } void fun_swap(int *p,int *q){ int t *q; *q *p; *p t; }数组作为参数传递时传的是数组的首地址。5.函数的传参练习字符串统计大小写转换并统计字母数量 #include stdio.h int fun_string(char *p); void fun_string_con(char *p,int *sum_p); int main(){ char s[100]; int sum 0; scanf(%s, s); fun_string_con(s,sum); /* sum fun_string(s); printf(%s %d\n,s,sum); */ printf(%s %d\n,s,sum); return 0; } /*int fun_string(char *p){ int sum 0; while(*p ! \0){ if(*p a *p z){ sum; *p - 32; }else if(*p A *p Z){ sum; *p 32; } p; } return sum; } */ void fun_string_con(char *p,int *sum_p){ while(*p ! \0){ if(*p a *p z){ (*sum_p); 针对sum改成了直接指针修改 *p - 32; }else if(*p A *p Z){ (*sum_p); *p 32; } p; } }一维数组在函数间传参void fun(int *arr, int size);void fun(int arr[ ], int size);编写函数计算一维数组所有整数的和 #include stdio.h int fun_sum(int * data, int n); int main (){ int a[] {1,5,9,10,20,21}; int sum 0; sum fun_sum(a,sizeof(a) / sizeof(int )); printf(%d\n,sum); return 0; } int fun_sum(int * data, int n){ int i; int sum 0; for(i 0; i n; i){ sum data[i]; } return sum; }排序对整形数组排序 #include stdio.h void fun_sort(int * ,int ); void fun_printf(int * ,int); int main(){ int a[] {1,5,9,10,20,21}; fun_sort(a,sizeof(a) / sizeof(int)); //fun_printf(a,sizeof(a) / sizeof(int)); int i; for(i 0; i sizeof(a) / sizeof(int); i){ printf(%d\t,a[i]); } return 0; } void fun_sort(int * data,int n){ int i,j,t; for(i 0; i n - 1; i){ for(j 0; j n - 1 - i; j){ if(data[j] data[j 1]){ t data[j 1]; data[j 1] data[j]; data[j] t; } } } } //void fun_printf(int * data, int n){ 这里data虽然是数组名但是这里变成指针了 int i ; for(i 0; i n ;i){ printf(%d,data[i]); } }字符数组传参函数删除字符数组里的空格 #includestdio.h void del_space(char *p); //char * del_space(char *s); int main (){ char s[100] asdf DDD sd; del_space(s); printf(%s\n,s); // printf(%s\n,del_space(s)); return 0; } void del_space(char *s){ //char * del_space(char *s){ char * p s; char * q s; while (*q ! \0){ if(*q ! ){ *p *q; p; q; }else{ q; } } *p \0; // return s; }二维数组用一级指针传参#includestdio.h void print_array(int *a, int n, int m); int main() { int a[3][4] {{1,5,3,2}},{3,6,9,8},{},{} }; print_array(a[0],3,4); return 0; } void print_array(int *a, int n, int m){ int i; for(i 0; i n * m; i){ printf(%d\n,*(a i)); i; } }二维数组用行指针传参#includestdio.h const int M 4; void print_array(int *a,int n,int m) // int get_odd(int a[][M],int n, int m); int get_odd(int (*a)[M], int n,int m); int main() { int a[3][4] {{1,5,3,2}},{3,6,9,8},{},{} }; print_array(a[0],3,4); int r; r get_odd(a,3,4); printf(r %d\n,r); return 0; } void print_array(int *a, int n, int m){ int i; for(i 0; i n * m; i){ printf(%d\n,*(a i)); i; } } int get_odd(int a[][M],int n,int m){ int i,j,sum 0; for(i 0; i n; i){ for(j 0; j m; j){ if(a[i][j] % 2 ! 0){ sum; } } } return sum; }二维数组用二级指针传参#includestdio.h const int M 4; void print_array(int *a,int n,int m) // int get_odd(int a[][M],int n, int m); int get_odd(int (*a)[M], int n,int m); int get_odd2(int **p,int n,int m); int main() { int a[3][4] {{1,5,3,2}},{3,6,9,8},{},{} }; print_array(a[0],3,4); int r; int *p[3] {a[0],a[1],a[2]}; r get_odd2(p,3,4); printf(r %d\n,r); return 0; } void print_array(int *a, int n, int m){ int i; for(i 0; i n * m; i){ if((i 1) % M 0){ putchar(\n); } printf(%d\n,*(a i)); i; } } int get_odd(int a[][M],int n,int m){ int i,j,sum 0; for(i 0; i n; i){ for(j 0; j m; j){ if(a[i][j] % 2 ! 0){ sum; } } } return sum; } int get_odd2(int **a,int n,int m){ int i,j,sum 0; for(i 0; i n; i){ for(j 0; j m; j){ if(*(a[i] j) % 2 ! 0){ sum; } } } return sum; }6.指针函数指针函数返回值是指针如int * fun(int x, int y) 返回值类型是int *数据类型 * 函数名 参数{ }#include stdio.h char * get_string() { char * s hello; return s; } char * get_char_array(){ char ch[10] {a, b, c, \0}; return ch; } int main() { char * s; s get_char_array(); 函数运行完局部变量就被释放会报错 char *p; p get_string(); printf(p %s\n,p); 字符串常量存在静态存储区函数结束了仍然可以访问地址 return 0; }字符串拷贝函数char *strcpychar *dest,const char *src;#includestdio.h #includestring.h int main() { char s1[100]; char s2[100]; char *s3 welcome; 字符串常量存在静态存储区 strcpy(s2,strcpy(s1,s3)) // printf(%s\n,strcpy(s1,s2)); 输出welcome printf(%s\n,s1); 输出welcome printg(%s\n,s2); welcome return 0 }指针函数的返回值#include stdio.h #include string.h char s[20]; 可以定义全局变量地址。 char * mystring(){ //char s[20]; 函数结束地址就被释放了报错 strcpy(s,hello); return s; } char * mystring2(){ char * s hello; 字符串常量的地址返回有效 return s; } char * mystring3(char * s){ 传给他的地址再返回地址当然有效 *s A 输出Aello return s; } int main(){ printf(%s\n,mystring()); return 0; }指针函数可以返回什么样的指针全局变量的地址、字符串常量的地址、static变量的地址、堆的地址、主调函数中有效内存指针函数编写字符串拷贝函数#include stdio.h #includeassert.h char * my_strcpy(char * dest,const char * src); char * my_strcpy2(char * dest,const char * src); int main (){ char s1[100]; char s2[100]; char *s3 welcome; //my_strcpy(s1,my_strcpy(s2,s3)); my_strcpy2(s1,my_strcpy(s2,s3)); puts(s1); puts(s2); puts(s3); return 0; } char * my_strcpy(char * dest,const char * src){ char * s dest; //先保存dest起始地址 while(*src ! \0){ *dest *src; } *dest \0; //或*dest *src; } return s; //此时dest已经移动了 } char * my_strcpy2(char * dest,const char * src){ char * s dest; //先保存dest起始地址 assert(dest src); 如果dest和src有一个为空会报警 while(*dest *src) //表达式不等于\0就会一直循环 //*dest \0; //或*dest *src; 可以不加了。 } return s; //此时dest已经移动了 }指针函数编写字符串连接函数#includeassert.h #include stdio.h //#include string.h char * my_strcat(cjar *dest,const char *src); int main () { char s1[100] ab; char s2[100] cd; char * s3 welcome; // strcat(s1,strcat(s2,s3)); my_stract(s1,mystrcat(s2,s3)); puts(s1); puts(s2); puts(s3); return 0; } char * my_strcat(char *dest,const char *src){ assert(dest src ! null); char *s dest; while(*dest ! \0){ //whi le(*dest); dest--; dest; } while(*src ! \0){ //while(*dest *src); *dest *src; src; dest; } *dest *src; return s; }指针函数案例编写函数把整数123改成字符串123.#include stdio.h char * my_itoa(int,char *) int main (){ int a 456; char s[100]; my_itoa(a,s); puts(s); return 0; } char * my_itoa(int a,char *s){ int i 0; int j 0; while(a ! 0){ s[i] a % 10 48; i; a a / 10; } s[i] \0; i--; while(j i){ t s[j]; s[j] s[i]; s[i] t; j; i--; } return s; }int add(int a, int b) { return a b; } int main() { int (*p)(int, int) add; int result (*p)(int, int); // 调用p指向的add函数 return 0; }
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2608947.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!