10 c语言指针
10.1 指针
10.1.1 指针的概念
指针变量也是一个变量;
指针存放的内容是一个地址,该地址指向一块内存空间
10.1.2 指针变量的定义
可以定义一个指向一个变量的指针变量
Int *p; //表示定义一个指针变量
*p; //代表指针所指内存的实际数据
切记,指针变量只能存放地址,不能将一个int型变量直接赋值给一个指针。
Int *p = 100;
Int *p = &a; //得到变量a的地址,将这个地址赋值给变量p
Int *p1;//定义一个变量,名字叫p1,它可以指向一个int的地址
P1=&b;//指针变量的值一般不能直接赋值一个整数,而是通过取变量地址的方式赋值
10.1.3 &取地址运算符
&可以取得一个变量在内存当中的地址
10.1.4 无类型指针
定义一个指针变量,但不指定它指向具体哪种数据类型。可以通过强制转化将void *转化为其他类型指针,也可以用(void *)将其他类型指针强制转化为void类型指针。
Void *p;
10.1.5 NULL
NULL在C语言中的定义为(void *)0;
当一个指针不指向任何一个有效内存地址的时候,我们应该把指针设置为NULL。
10.1.6 空指针与野指针
指向NULL的指针叫空指针,没有具体指向任何变量地址的指针叫野指针。
空指针是合法的,但野指针是危险的,是导致程序崩溃的主要原因。
10.1.7 指针的兼容性
指针之间赋值比普通数据类型赋值检查更为严格,例如:不可以把一个double *赋值给int *;
原则上一定是相同类型的指针指向相同类型的变量地址,不能用一种类型的指针指向另一种类型的变量地址。
10.1.8 指向常量的指针与指针常量
Const char * p; //定义一个指向常量的指针
Char *const p;//定义一个指针常量,一旦初始化之后其内容不可改变。
10.1.9 指针与数组的关系
一个变量有地址,一个数组包含若干个元素,每个元素在内存中都有地址。
Int a[10];
Int *p = a;
比较p和&a[0]的地址是否相同。
10.1.10 指针运算
指针运算不是简单的整数加减法,而是指针指向的数据类型在内存中占用字节数作为倍数的运算。
Char *p;
P++; //移动了sizeof(char)这么多的字节数
int *p1;
P1++; //移动了sizeof(int)这么多的字节数
赋值:int *p = &a;
求值:int i = *p;
取指针地址int **pp = &p;
将一个整数加(减)给指针:p+3;p-3;
增加(减少)指针值 p++,p–
求差值,p1-p2,通常用于同一个数组内求两个元素之间的距离。
比较p1= =p2,通常用来 比较两个指针是否指向同一个位置。
10.1.11 通过指针使用数组元素
P+1代表&a[1],也可以直接使用p[1]表示a[5]
P +5代表&a[5];
P++
寻找数组第二大元素
第一步:假设数组中前2个元素就是最大的和第二大的
Max
Smax
第二步:从数组的第2号元素开始遍历数组
当有元素大于max的时候,
Smax = max
Max= 最大的那个元素,
如果当前元素小于max,并且大于smax,那么就让smax等于当前那个元素
int smax(int *s)//求数组中第二大元素
{
int max = 0;
int s_max = 0;
int i;
if (*s > *(s +1))
{
max = *s;
s_max = *(s + 1);
}
else
{
max = *(s + 1);
s_max = *s;
}//将max等于s[0]和s[1]中大的那个元素的值
for(i = 2;i < 10;i++)//从第3个元素开始遍历数组
{
if(max < *(s + i))//如果遇到大于max的元素,那么让s_max等于max,让max等于这个元素
{
s_max = max;
max = *(s + i);
}
else if(max > *(s + i) && *(s + i) > s_max)//如果这个元素是介于max和s_max之间,那么就让这个元素等于s_max
{
s_max = *(s + i);
}
}
return s_max;//返回s_max的值
}
int main()
{
int buf[10] = {34,21,56,4,87,90,15,65,72,48};
printf("%d\n",smax(buf));
return 0;
}
#include<string.h>
//通过指针将字符串逆置
int main()
{
char str[100]="you";
char *str_start = &str[0];
char *str_end = &str[strlen(str) - 1];
while(str_start < str_end)
{
char *tmp = * str_start;
* str_start = * str_end;
* str_end = tmp;
str_start ++;
str_end --;
}
printf("%s\n",str);
return 0;
}
对于VS的汉字是GBK编码,一个汉字2个字节;对于QT汉字是UTF8编码,一个汉字是3个字节。
#include<string.h>
//通过指针将汉字字符串逆置
int main()
{
char str[100]="我爱你";
short *str_start = &str[0];
short *str_end = &str[strlen(str) - 2];
while(str_start < str_end)
{
short *tmp = * str_start;
* str_start = * str_end;
* str_end = tmp;
str_start ++;
str_end --;
}
printf("%s\n",str);
return 0;
}
10.1.12 指针数组
int *a[10];//定义了一个指针数组,一共10个成员,其中每个成员都是int *类型
10.1.13 指向指针的指针(二级指针)
指针就是一个变量,既然是变量就也存在内存地址,所以可以定义一个指向指针的指针。
通过二级指针修改内存的值;
Int I = 10;
Int *p1 = &I;
Int **p2 = &p1;
Printf(“%d\n”,**p2);
|
以此类推可以定义3级甚至多级指针。
C语言允许定义多级指针,但是指针级数过多会增加代码的复杂性,考试的时候可能会考多级指针,但是实际编程的时候最多用到3级指针,但是3级指针也不常用,一级和二级指针是大量使用的。
10.1.14 指向二维数组的指针
{
int buf[3][5] = {{2,4,3,5,1},{7,2,6,8,1},{7,3,9,0,2}};
int i;
int j;
int sum;
for(i = 0;i < 3;i ++)
{
sum = 0;
for(j = 0;j < 5;j ++)
{
sum += (*(*(buf + i) + j));
//sum += buf[i][j];
}
printf("%d\n",sum / 5);
}
for(i = 0;i < 5;i ++)
{
sum = 0;
for(j = 0;j < 3;j ++)
{
sum += (*(*(buf + j) + i));
//sum += buf[j][i];
}
printf("%d\n",sum / 3);
}
return 0;
}
10.1.15 指针变量作为函数的参数
函数的参数可以是指针类型 *,它的作用是将一个变量的地址传送给另一个函数。
通过函数的指针参数可以间接的实现形参修改实参的值。
10.1.16 一维数组名作为函数参数
当数组名作为函数参数时,C语言将数组名解释为指针
int func(int array[10]);//数组名代表数组的首地址
10.1.17 二维数组名作为函数参数
二维数组做函数参数时可以不指定第一个下标。
int func(int array[][10]);
将二维数组作为函数的参数用例不是特别多见。
10.1.18 const关键字保护数组内容
如果讲一个数组作为函数的形参传递,那么数组内容可以在被调用函数内部修改,有时候不希望这样的事情发生,所以要对形参采用const参数。
func(const int array[]);
10.1.19 指针作为函数的返回值
return NULL;
10.1.20 指向函数的指针
指针可以指向变量、数组,也可以指向一个函数。
一个函数在编译的时候会分配一个入口地址,这个入口地址就是函数的指针,函数名称就代表函数的入口地址。
函数指针的定义方式:int (*p)(int);//定义了一个指向int func(int n)类型函数地址的指针。
1 定义函数指针变量的形式为:函数返回类型(指针变量名称)(参数列表)
2 函数可以通过函数指针调用
3 int( p)()代表指向一个函数,但不是固定哪一个函数
Void *p(int ,char *);//声明了一个函数,函数的名字叫p,函数的返回值是void *,函数的参数是int和char *
Void (*p)(int ,char *);//定义了一个指向参数为int和char *,返回值为void的函数指针
Int *(*p)(int *);//定义一个参数为int *,返回值为int *的指向函数的指针
Int *p(int *);//声明了一个函数,返回值是int *,参数是int *
Int (*p)(int , int );//定义了一个指向函数的指针,可以指向两个参数,都是int,返回值也是int类型
在回调函数和运行期动态绑定的时候大量的用到了指向函数的指针。
10.1.21 把指向函数的指针作为函数的参数
将函数指针作为另一个函数的参数称为回调函数。
int max(int a,int b)
{
if(a > b)
return a;
else
return b;
}
int add(int a,int b)
{
return a + b;
}
int func(int (*p)(int,int),int a,int b)//第一个参数是指向函数的指针
{
return p(a,b);//通过指向函数的指针调用一个函数
}
int main()
{
int i = func(add,6,9);//add函数在这里就叫回调函数
printf("i = %d\n",i);
return 0;
}
10.1.22 memset,memcpy,memmove函数
这三个函数分别实现内存设置,内存拷贝,内存移动。
使用memcpy的时候,一定要确保内存没有重叠区域。
#include <sting.h>
memset(buf, 0, sizeof(buf));//第一个参数是要设置的内存地址,第二个参数是要设置的值,第三个参数是内存大小,单位:字节。
memcpy(buf2, buf1, sizeof(buf1));//将buf1的内存内容全部拷贝到buf2,拷贝大小为第三个参数:字节。
memmove(buf2, buf1, sizeof(buf1));//并没有改变原始内存的值。
10.1.23 指针小结
写在最后:最近和朋友一起在微信公众号做一些自己热爱的东西,并有许多的干货分享,大家多多关注啊!!
公众号 [越陌的数字生活]