指针_C语言

基础

  • 内存:是通过连续的内存编号来管理的(内存的地址)
  • 指针:指针变量里边存储的就是内存地址

值和类型

不能简单通过检查一个值的位来判断它的类型,值得类型取决于其使用方式

未初始化和非法指针

int *a;
*a = 12;

访问未初始化的指针是非法的

指向指针的指针

int a = 12;
int *b = &a;
int **c = &b;
*操作符具有从右向左的结核性,所以这个表达式相当于*(*c)

定义

*a 间接访问

通过一个指针访问它所指向的地址的过程称为间接访问(indirection)或解引用指针(dereferencing the pointer),这个用于执行间接访问的操作符是单目操作符*

int a = 10;
int *p = &a;
  • int *p表示:表达式*p产生的结果类型是int类型
  • int *并不是一个类型,原因如下
int* b,c,d;

这并不是将三个变量声明为指向整型的指针,b是一个指针,其余两个变量为普通整型

&b 取地址符

int *pb = &b;// & scanf函数中有一个取地址符指针变量可以通过地址赋值

%p 指针占位符

printf("%p\n",p);

NULL指针

对一个NULL指针进行解引用操作是非法的

NULL类似于给普通变量赋初始值的0,其实就是地址为0

int *p =NULL;//定义了一个指针变量,初始值为NULL
float*p1 = NULL;//浮点型指针
double *p2 =NULL;
char *p3 =NULL;

指针的算术运算

int *pp =NULL;
int num1 = 10;

pp = &num1;

printf("%p\n",pp);
printf("%p\n",(pp+1));

指针的算术运算需要根据指针类型去计算,指针+ 1是向高位移动一个类型所占的字节数,指针- 1是向低位移动一个类型所占的字节数

printf("%p\n",pp++);
printf("%p\n",++pp);
printf("%p\n",pp);

++ --还是遵循之前++ --运算符法则

指针的重指向

咱们可以让指针重新指向另一个变量(内存的另一块区域),这就叫指针的重指向
我们可以通过指针修改内存地址上的值

int number1 = 10;
int *pNum1 = &number1;

int number2 = 8;
pNum1 = &number2;

*pNum1 = 9;
printf("%d\n",*pNum1);
printf("%d\n",number2);

用指针交换两个数的值

  • 函数在传参数的时候,形式参数只是拷贝了实际参数的内容或者值,实际参数并未发生改变
  • 有了指针我们可以直接操作指针指向的内存区域,然后修改内存中的值
int num1 = 12;
int num2 = 16;
swapTwoNumbers(&num1, &num2);
printf("%d %d\n",num1,num2);

指针与数组的关系

数组名就是数组的地址,它恒等于数组元素的首地址

实际上*(arr1 + i) = arr[i];

int arr1[4] = {1,2,3,4};
for(inti = 0; i < 4; i++) {
    printf("%p\n",arr1+0);
}

printf("%lu\n",sizeof(arr1));//数组名虽然是地址,但是我们利用它计算内存空间的话,还是根据里边存储的数据类型而定

int *pp = &num1;

printf("%lu\n",sizeof(pp));//打印一个单纯的变量地址的话,并不是根据类型而定

printf("%lu\n",sizeof(&num1));

char aa = 'w';
char *pp1 = &aa;

printf("%lu\n",sizeof(pp1));//指针类型所占的字节数与计算机本身的计算位数有关,如果是32位的计算机,指针类型占4个字节,如果是64位的计算机,指针类型占8个字节
   
short array[4] = {1,2,3,4};

int *p = array;//不匹配的指针类型

数组名确实能够打印出数组元素的首地址,但是如果把数组名认为就是地址是不准确的,实际上数组名是一个常量

指针与字符串的关系

char name[] ="guozhenyan";
char *p1 = name;

printf("%p\n",p1);

for(int i = 0; i <strlen(name); i++) {
    printf("%c\n",*(p1+i));
}

指针数组

char *a[10] = {"ddd","sdq","das","qwe","cva","btg","aaa","ccc","eee","fff"};

printf("%p\n",a);//数组指针的地址
printf("%p\n",a+1);//对数组指针地址进行操作
printf("%p\n",a[0]);//对存的数据地址进行操作
printf("%p\n",a[1]);

printf("%p\n",*a);//存的是地址
printf("%p\n",*(a+1));

printf("%c\n",**a);
printf("%c\n",**(a+1));

printf("%c\n",*a[0]);
printf("%c\n",*a[1]);

结构体指针

typedef struct person {
   char name[20];
   int age;
   int score;
}Person;

Person p1 = {"guangguang",16,90};
Person p2 = {"jintao",18,99};
Person p3 = {"wangce",17,88};
      
void printArr(Person*p,intcount){
for(int i = 0; i < count; i++) {
printf("%s,%d,%d\n",(*(p+i)).name,(p+i)->age,(*(p+i)).score);
}
    //使用指针去访问结构体数组的成员变量的时候,可以使用-> 
Person arr[3] = {p1,p2,p3};

函数指针

函数名跟数组名类似,实际上它也是一个地址

printf("%p\n",helloWorld);

我们也可以找一个指针,指向函数名所在的地址.这个指针就是所谓的函数指针

函数的指针类型根据函数的返回值和参数而定
返回值类型+ (*指针名) +参数的类型=函数指针

void (*p1)() =NULL;
p1 =helloWorld;//声明
p1();//调用方式
void (*p2)(int) =NULL;

p2 =printNumber;
p2(31415);

BOOL (*p3)() =backYes;

printf("%d\n",p3());

int (*p4)(int,int) =sum;
printf("%d\n",p4(8,4));

函数指针可以重新指向别的函数,只要函数类型一样就可以重指向

char a[10];

printf("请输入函数名:\n");

scanf("%s",a);

int(*p5)(int,int) =NULL;

printf("请输入a,b的值:\n");

int b,c;
scanf("%d%d",&b,&c);

if(strcmp(a,"maxValue") == 0){
p5 =max;
}else if(strcmp(a,"sum") == 0){
p5 =sum;
}else{
printf("输入有误\n");
return 0;
}
//现在p5这个指针并没有指向认可地址,也可以导致程序崩溃,这种情况叫作野指针

printf("%d\n",p5(b,c));

替换指针类型

typedef int (*pGet)(int,int);//重新定义函数指针的类型:pGet
//我们利用typedef这个关键字还可以重新定义函数指针的类型变成一个新的类型*后边跟上我们新类型的名字

函数指针的作用

  1. ️函数指针是OC中block的底层实现,OC中block得形式跟函数指针一模一样.
  2. ️函数指针因为可以重指向不同函数,可以起到简化代码和解耦合的作用

函数回调(用函数指针作为函数参数)

int(*p)(int,int) =NULL;
p = max;//函数指针指向的函数可能实现的功能比较复杂,或者是公司核心代码,我们一般程序员不需要知道里面的详细内容,只需要会调用即可.(面向对象语言,封装特性的底层(雏形))

函数转移表

//常规写法
switch ( oper ){
    case ADD:
        result = add( oper1, oper2 );
        break;
    case SUB:
        result = sub( oper1, oper2 );
        break;
    case MUL:
        result = mul( oper1, oper2 );
        break;
    case DIV:
        result = div( oper1, oper2 );
        break;
    ...
}
//使用转移表之后的写法
double add( double, double );
double sub( double, double );
double mul( double, double );
double div( double, double );
...
double (*oper_func[])( double, double ) = {
    add, sub, mul, div, ...
};
//下面的语句替换前面整条switch语句
result = oper_func[ oper ]( op1, op2 );

范指针

void *p;
memcpy;

类型转换

fptr = (float *) iptr;
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容