1·C语言 您所在的位置:网站首页 c语言的且符号怎么打出来 1·C语言

1·C语言

2024-06-14 02:00| 来源: 网络整理| 查看: 265

1·指针到底是什么? 指针其实也是一种变量,叫做指针变量。指针变量与其他的变量本质上一致。

2·指针变量三部曲 定义指针变量、绑定指针、解引用指针

int a =5; //定义指针变量 int *p ; //绑定指针的第一种方法 p = &a; // 绑定这种的第二种方法 p = 4;//这种绑定方式错误,因为p是int *类型, //4 是int 类型,类型不匹配所以不能这样绑定 p = (int *)4;// 绑定这种的第二种方法,把int类的4 强制转换为 // 一个地址(也就是int *类型的),这样可以使类型匹配。 //解引用指针 *p = 233;

注意:如果指针变量没有被绑定,那么就不能解引用指针。原因:c语言局部变量的一般规律是{定义局部变量并且没有初始化,则值是随机的},所以p这个指针变量中存储的是一个随机的数字。此时如果我们解引用p,相当于我们访问了这个随机数字为地址的内存空间,这个随机内存空间能不能被访问我们是不知道的,所以程序容易出问题。

3·关于指针的符号 第一个符号,星号 * 。*星号有两种用法,第一种就是与常用的数据类型结合,表明定义的是一个指针。第二种用法就是, *变量名。 表示对这个指针变量解引用。

第二个符号,&。表示取地址符。

int a = 23; int b = 0; int *p; // 表示定义了一个指针变量p p = &a; // 把a的内存地址传给指针p(p指向了a的地址) b = *p;// 解引用这种变量p // 就是把p指向的地址(就是a)所存储的数据 赋值 给b 结果: b = 23

区分:

int *p5,*p6;// 定义的是两个这种变量 p5,p6 int *p5,p6;// 定义的是p5指针变量和p6为int的普通变量

4·指针定义并且初始化 与 指针定义然后赋值 的区别

//指针变量定义同时初始化 int a = 32; int *p = &a; //指针变量定义(指针变量没有初始化)后再赋值 int a = 32; int *p; p = &a; // 正确 *p = &a //错误 错误原因:*p 是表示对指针变量p的解引用, 解引用后可以认为表示一个int类型的数据 (因为例子中就是定义的一个 int *类型的指针), 而&a表示取出a对应的内存空间的地址。所以类型不匹配。

5·左值与右值 再赋值运算符的左边叫左值,右边叫右值。左值表示内存空间,右值表示变量的值。

a = 3; b=5; 1. a = b 表示 把b内存空间所存储的值赋值给a对应的内存空间,a=5 2. b = a 表示 把..... b=3

6·野指针 6·1 什么是野指针? 指针指向的位置是不可知的(随机的,不正确 的,没有明确限制的) 6·2 危害: 引发段错误。 6·3 野指针的来源: 指针定义后没有初始化,也没有赋值,然后就去解引用。 6·3 如何避免? 在指针的解引用之前一定确保指针指向一个绝对可用的空间。 6·5 常规做法: 第一步,定义指针时,同时初始化为NULL。 第二步,在指针解引用之前先去判断这个指针是不是NULL. 第三步,在指针使用之前,将其赋值绑定给一个可用地址空间。 第四步,指针使用完后将其值赋值为NULL。

int *p = NULL; int a ; p = &a; //之间省略许多代码。。 if(NULL != p)// 注意:NULL在不等号前面,编译器会更好的检查。 { *p = 4; }

6·6 NULL到底是什么? C++中,NULL表示 0 C中,NULL表示强制类型转换为 void * 的 0.(viod 表示任意) 所以,NULL的实质就是 0,我们把指针赋值为NULL,就是让指针指向0地址,是为了(1)0地址作为一个特殊地址,我们认为这种指向这里表示指针没有被初始化,就表示野指针,虽然这个指针是野指针,但是他不会乱指向我们不知道的地方,就相当于用0地址将野指针束缚住了。(2)0地址在一般的操作系统系统中都是不可访问的,如果直接解引用就会引发段错误。

7·const关键字与指针 7·1 const修饰指针的4种形式: 首先,对于指针变量的理解:主要设及两个变量,一是指针变量p本身,二是p指向的那个变量(也就是*p)。 一个const关键字智能修饰一个变量,所以理清楚4个表达式的关键就是搞清楚const放在某个位置是修饰谁的。

const int *p //p本身不是const的,而p指向的变量是const的 int const *p //p本身不是const的,而p指向的变量是const的 int * const P //p本身是const的,而p指向的变量(*p)不是const的 const int * const p //p本身是const的,p指向的变量也是const的

不能被改变

7·2 const修饰的变量真的就不能被改变吗?

int main(void) { const int a = 5; // a = 88; 这样直接改是肯定不行的,因为const管住了变量a //用指针的方法,解引用指针来改变。 int *p; // p = &a; // 编译时会有警告。但是可以改变const管住的变量的值 //警告的原因:因为const的存在,所以不能这样去做 p = (int *)&a;// 强制转换 消除警告 *p = 88; return 0; }

所以,const修饰的变量可以改。前提是在gcc环境下。 在某些单片机环境下,const管住的变量就不可以。

补充一下数组的知识: 在这里插入图片描述 *上面这个图 :&a[0]做左值和右值都讲错了。&a[0]表示数组首元素首地址,是个常量所以不能做左值;做右值时就表示数组首元素首地址的值,等同于a做右值。 *

这是一题面试题:考点就是数组名a。 a 与 &a 表示的含义是什么?含义不同,导致偏移量的偏移基础不同。 在这里插入图片描述

#include int main() { int a[4]={1,2,3,4}; int *p = (int *)(&a + 1); printf("%d,%d\n",*(a+1),*(p-1)); return 0; } 结果: 2,4

为了形成对比,我们改一下上面的代码的int *p = (int *)(&a + 1);把它的&去掉,来验证 a 与&a 的含义不同。

#include int main() { int a[4]={1,2,3,4}; int *p = (int *)(a + 1); printf("%d,%d\n",*(a+1),*(p-1)); return 0; } 结果: 2,1

分析: &a 表示整个数组的首地址。(&a + 1)就表示偏移量 1 是以一个a数组为单位。int *p = (int *)(a + 1);就表示如下 在这里插入图片描述

a 表示数组首元素首地址,(a + 1)就表示偏移量 1 是以数组的元素为单位偏移的。当然,int *p = (int *)(a + 1);就是在数组首元素基础上偏移一个元素。如下图。 在这里插入图片描述

思考: int *p = (int *)a + 1; 这种形式会不会让p偏移到只距离数组首元素地址一个字节的地方?(如下图) 在这里插入图片描述 其实是不能的!!! 因为有两点:第一,我们第一的是int类型的指针p,所以解析方式应该是以4个字节为单位。第二,a表示数组首元素地址,(a+1)偏移量 1 也应该是以数组元素类型偏移,也就是4个字节;更别说&a表示整个数组的首地址了,(&a+1)偏移量 1 就该是以整个数组为单位偏移,也就是4X4=16个字节偏移。

那有没有可以让(a+1)就表示偏移一个字节的办法?有啊,你把a数组定义成一个char类型的数组就可以了啊哈哈哈哈。所以,这就更加证明了类型只是变量的解析方式。

8·指针与数组的交织 8·1 访问数组的两种方式 第一种就是数组访问,第二种就是这种访问。

int main (void) { int a[4] = {1,2,3,4}; printf("a[2] = %d\n",a[2]); printf("a[3] = %d\n",*(a+3));//指针方式访问 // 这三句代码就是上面指针访问方式的拆开 int *p; p = a;//注意a作为右值的意义。 //a为数组名,表示整个数组的内存空间, //由于整个数组是按一个元素一个元素使用的,所以a不能做左值。 //a做右值表示数组首元素首地址。等同于 &a[0]. printf("a[3] = %d\n",*(p+3)); return 0; }

8·2 指针与数组类型的匹配问题

int *p; int a[5]; p = a;// 类型是匹配的 p = &a;//类型不匹配

p = &a 类型不匹配的原因是: p是int *类型,&a 表示整个数组的内存空间,是整个数组的指针,也就是一个数组指针的类型,不是int *类型,所以不匹配。

&a,a, &a[0] 的含义: 三者从数值上面来看是完全相等的。 从意义上来看,a 与 &a[0] 是数值首元素首地址,而&a 是整个数组的首地址。 从类型来看,a 与 &a[0] 是元素的指针,也就是int *类型的;而 &a 是数组指针,是int ( *a)[5] 类型。

8·3 指针类型决定了指针如何参与运算 (1)指针参与运算时,因为指针变量本身存储的是地址,所以运算也是地址的运算。 (2)指针参与运算的特点:指针变量+1,并不是真的+1,而是 + 1*sizeof(指针类型);如果是int *类型的指针,则+1就是实际表示 地址+4,如果是char * 类型的指针,+1 就实际表示地址+1;如果是double *类型的指针, +1 就是 实际 地址 +8。

9· strlen 与 sizeof strlen 不计入字符串的结尾标志 \ 0。 sizeof 则计入字符串结尾标志 \0. 32位系统的指针变量的长度是 4 个字节。

10· 重定义 入图片描述

11·指针与函数传参 11·1 普通变量作为函数形参 “实参做右值,形参做左值” -----传值调用 11·2 数组作为函数形参 数组名作为形参传参时,实际传递的不是整个数组,而是数组的首元素的首地址(也就是传了个指针)。所以子函数内部,传进来的数组名就是一个指向数组首元素首地址的指针。所以sizeof得到的是4.

在子函数内传参得到的数组首元素首地址,和主函数的数组首元素首地址的值是相同的------传址调用

11·3 指针作为函数形参 与数组作为函数形参是一样的。

11·4 结构体传参 结构体变量对齐

12· 传值调用 与 传址调用 传值调用:传的变量。主函数的变量值只是复制了一份一样的变量值,用于给swap1子函数传参。子函数swap1中交换的是复制后的变量值(只是在swap1交换函数内部交换),但是在主函数的原来的变量值并没有交换。

传址调用:传的是指针。主函数的变量依旧是不能进入到子函数swap2中,进去的只有一份拷贝。传址调用就是把主函数的变量的地址传进子函数swap2去,这样swap2子函数内可以通过指针解引用的方式访问主函数的变量,从而改变主函数的变量。

void swap1(int a,int b) { int temp; temp = a; a = b; b = temp; printf("in swap1,a = %d, b = %d\n",a, b); //子函数内部交换成功 } void swap2(int *a,int *b) { int temp; temp = *a; *a = *b; *b = temp; printf("in swap2,*a = %d, *b = %d\n",*a, *b); // 子函数与主函数结果一致。 } int main (void) { int x = 4,y = 9; swap1(x,y); printf("in swap1,x = %d, y = %d\n",x, y); // x = 4,y = 9 交换失败 swap2(x,y); printf("in swap2,x = %d, y = %d\n",x, y); // x = 9,y = 4 交换成功 return 0; }

另外知识点 理解 输入型参数 与 输出型参数



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有