c语言,指针6.13

本文详细介绍了C语言中指针的概念,涉及内存地址、访问变量的两种方式、指针的定义与初始化、数组与函数指针,以及它们在动态内存分配、多返回值和数据结构中的应用。通过实例演示,帮助理解指针在程序中的关键作用和潜在风险。
摘要由CSDN通过智能技术生成

c语言,指针6.13

引言:最近要完成c语言课程设计,整理复习下指针和结构体链表。

要做便认真吧,加油做完。做完 > 做好。

沉学。

1.指针的概念。

指针是c语言中的一个重要概念,它能 有效地 表示 和 处理 复杂的数据结构

特别擅长于 处理 动态数据结构

正确而灵活地运用指针可以有效的实现一些重要的功能。

比如 动态内存分配从函数获得多个返回值等

指针用法灵活,能使程序更加简洁高效,同时指针也是一个容易引起混淆的概念,

使用不当会导致一些隐蔽而严重的程序问题。

程序运行时,变量存放在内存中,不同的变量存放在内存不同的位置,这个位置是可以标示的。

在c语言中,使用指针来表示变量在内存中的位置。

指针与变量的关系类似于 门牌和房屋的关系。

指针记录了对应变量的物理内存地址,

靠指针就可以访问对应的物理内存地址,也就访问了变量。

变量在内存中的位置,就是变量的指针。 指针,即是,地址,(内存)(物理编号)地址。

2.内存

内存以字节为单位。 1 byte = 8 bit (位).

一个字节,1 byte,就是一个内存单元,每个内存单元有一个不重复的编号,称为内存地址。

内存地址是一个无符号整数,相邻的地址常常是连续的。

变量根据数据类型 datatype 被分配一个Or多个内存单元,这些内存单元在地址上连续。

变量的值(内容)被存在这些单元中。

定义变量后,对内存的访问实质是访问变量所占用的内存单元。

3.访问变量的两种方式

int a=67;

int *p = &a;

1.直接访问

用变量名

a = 77;

2.间接访问

用指针

*p = 77;

&:取地址符,运算符的一种,提供后变量的地址。

与取值符 * 是逆操作。

*:

1.乘法运算符

2.指针类型标示符。

用于指针的定义时标示此为指针变量,类型、。

3.取值运算符

与 & 取地址符是逆操作。

效果是提供后地址所存储变量的值(内容)。

其中,1用于可以乘法运算的数值(变or常量)之间。(c语言的字符类型在乘法中会转换为对应的int 类型 ASC码值。

2和3应用于指针场景。

2只存在于指针定义时。

int a = 67,* pa = &a;

int 表示变量类型(or指针指向的变量类型)

*表示后面的为指针变量名。

另一种初始化定义。

int a =67,*pa;

pa = &a;//此处不能为 *pa,之前的 * 是 指针类型的标识符。

4.指针的定义,初始化方法格式。

1.指向变量。

#include<stdio.h>
int main(){
    int a = 97 , *pa = &a;
    int b = 10, *pb;
    pb = &b;
    char c = 97;
    char *pc = &c;
    printf("请输入a的值。");//输入提示。
    scanf("%d",&a);//只有scanf()是要取地址符&,printf不用。
    printf("a的值是 %d\n",*pa);
    printf("b的值是 %d\n",*pb);
    printf("c的值是 %d\n",*pc);
    printf("上面这个c的值是用的 %%d 整形格式符控制。下面才使用的 %%c,char类型格式控制符\n");
    printf("c的值是 %c\n",*pc);
    return 0;
}

//
a的值是 97
b的值是 10
c的值是 97
上面这个c的值是用的 %d 整形格式符控制。下面才使用的 %c,char类型格式控制符
c的值是 a
//
    大致就是
datatype *pointerName = &变量name;
唯有此处的 * 是指针类型表示符的含义。
    

唯有此处的 * 是指针类型表示符的含义。

唯有定义指针的时候。

2.指向数组

#include<stdio.h>
int main(){
    int a[5],*pa = a;
    int b, *pb = &b;
    //数组指针和普通变量指针不同。
    //数组名记录的是数组首元素的地址。不用&取地址。
    int *pB = pb;
    //直接用指针初始化。
    //
    //指针也可以被初始化为0or NULL;//NULL 全大写, null不行,NULL是<stdio.h>中定义的符号常量。
    double *pp = 0;
    float *pP = NULL;
    //无论是何种类型的指针变量,其中保存的都是所指对象的内存地址,内存地址长度是固定的,所以指针变量本身被分配的内存单元也是固定值,与指针类型无关。
    printf("指针类型的大小为:%d 字节",sizeof(int*));
    return 0;
}
//
我这里是指针8字节,这么多吗。64位系统默认指针是8字节。
    int* 就是指向Int的指针类型了,算一种独立类型。

注意:

指针类型必须和所存的地址存放的数据类型相同。

void指针,必须强制转化了才能用。

int a = 1;

void *pa = a;

print(“%d”,* (int *)pa);

//此处第一个*是取取值符号, 第二个 *是指针类型标示符号

运算符号:
&,

*间接访问(引用)运算符,右操作数必为指针。

[]下标运算符,取指针所指对象的值。左操作数必为指针。

指针与函数

1.指针做参数

void A(int * ,int a)

(注意:函数的参数传递是“值传递”,是单向传递,实参的值传递给形参,

然后形参在函数结束使内存空间被释放(局部变量,生命周期结束了)

只有返回值能够被记录返回出来实现传递一个值。一般只有一个返回值。

一return 语句 ,要想实现多返回,就要用指针。)

利用指针多返回值实现。

因为指针的存在关系,

不能(总不能)直接把指针的内容给自动释放了,会出事,

用指针,通过取指针的值的方式,直接访问到实际变量中的值。

而不是a = a` ,把实参a的值赋给形参a,

然后让形参a来变化计算,最后被释放,实参a还是a,实际上没有改变。

只是你函数经过了一系列操作,最后得到的数据变化方面的东西只有给你一个返回值。

而用指针不一样。

你把门牌号都给别人了,别人没有用a`,是直接去地址上的你家,去直接对a进行了操作。

所以操作得到了保留。

实现了(类似)多返回值的效果。

2.指针做返回值

int* pa;

return pa;

函数的定义

int * 函数name(参数表){函数体,}

datatype * name(参数表){body}

3.指向函数的指针。

datatype (*指针变量名) (形参表)

例子;

#include<stdio.h>
int add(int a,int b){
    return a+b;
}
int main(){
    int (*pointer1)(int,int);
    pointer1 = add;
    printf("%d",pointer1(5,5));
    return 0;
}
//把定义函数的格式中,函数名换为 *pointname
就可以表示该指针指向的对象类型了。
    是具有同样形参表和返回值类型的。才能赋值。实现调用。
    然后直接
    pointrtnzme = 函数name
    不用取地址,函数名也是指针,是函数的入口地址。
    多了一种调用方式,直接用 pointername(然后填参数就可以)

4.指针数组

int a = 0;
int * pa[5];
pa[0] = & a;
printf(“a的值为:%d”,*pa[0]);

定义指向函数的指针。

int (*pa)(int a,int * b)

int (*pA) ()[5];

定义了一个指向函数的指针数组pA,长度为5,不限定形参表,但是返回值还是要确定的。

小结

指针返回值 int* add()

指针形参 int add(int*,int) or int add(int * pa,int a);

指针数组 int* pa[5];

指针指向函数 int (*pa)(形参表)

指针指向函数数组 int (*pa)(参数表)[4]

指针就是地址,理论上只要是在内存中存在,他就可以指向标明。

就可以间接引用。

5.指针与一维数组

基本数据类型 按 一定的顺序 组成的集合 叫做 构造数据类型

数组是最基本的构造类型。

数组名是数组首元素的地址,是一个指针。

访问数组元素可以通过下标(运算符)或者移动指针来做到。

int a[5],*p;

p = a;

p = &a[0];

上面两种p的初始化效果一致,要么直接用数组名指针a去初始化,要么用数组的首元素取地址。

p指向数组中的元素后

可以通过运算来使物理地址增减,来实现移动指针,指向数组中的其他元素。(数组是连续排列的,这是必要前提,不然指向的就是与此数组元素相邻的不知道什么内容了。

p[i] 0r *(p+i),

通过这种方式可以访问元素P 后面第i个数组元素。

要访问前面就用

p[-i] or *(p-i);

p++,++p,–p,p–,都是有效的移动指针运算。

指针就是特殊的变量类型。

2.指针 的 关系运算。

#include<stdio.h>
int main(){
    int a[5] = {0},*p = a ;


    if(p<(p+1))
    printf("p在p+1的前面");
    else
    {
        printf("出错了。");
    }
    if(a+1 == &a[1])
    printf("p在p+1的前面");
    else
    {
        printf("出错了。");
    }
    if(a < &a[1])
    printf("p在p+1的前面");
    else
    {
        printf("出错了。");
    }
    if(a != &a[1])
    printf("p在p+1的前面");
    else
    {
        printf("出错了。");
    }
    return 0;
}
//结果皆是if成立。

p1>p2,

p1<p2,

p1 == p2

p1 != p2;

地址越大,说明越排在后面,地址递增吗。 0x0001,0x0002…这样排列。

==就是地址相同,指向同一元素,!=就是地址不同,指向不同元素(就算值相等,也不是一个元素,只是相同数值内容)。

就是合乎逻辑的普通关系,这里书上写错了。

p1 - p2 = -4;

p2 - p1 = 4;

则他们之间有(4-1)=3个元素,p1在p2的前面。有结果-1个元素

p1-p1=0,p2-p1=1;之间并没有元素。

指针间的加减可以得知,他们之间有多少元素(他们的排列位置相对关系,这样更准确。)。

–,++,*,&,的优先级相同。

(为防止错乱,最好还是用小括号括起来,来保证式子按理想的运算顺序执行)

6、指针和二维数组

在c语言中,二维数组被视为元素是一维数组的一维数组。

大概是存放元素都是一维数组的首地址。

先行后列。

a[1][2],行地址,列地址。//老样子注意从0开始。

元素和地址

对于变量。每* or [] 一次,就是取了一次值,每& 一次,就是取了一次地址。

比如

int a[5] = {0},*p = a;
那么, p == a == &p[0] == &(*(&p[0])) == &*&p[0];
//&*&p[0]是可以的,但是 &&*p[0] 是不行的,因为p[0]本身就是值了,不能在取值,不能往值方向走了。
//要注意这一点。
//&取地址就取他的地址,*取值就取他的值,如果没有,当然会报错。
//然后记得=和==是不一样的运算符,一个赋值,一个逻辑关系相等运算符。
int a[5][5] = {0},*p = a;
这里,
p = a;
p = a[2];
p = &a[2][2];
都是可以的。
分别是
指向首地址(这里,二维数组名实际上是一个二级指针,存放了一个地址,所以他可以被取2次值。
二维数组可以理解为一个指针数组。
所以二维数组的每行都是指针,可以直接给指针变量赋值。
    //以访问一维数组的方式访问二维数组元素。
那就直接
    p = a[2];
//p = a的第3行地址,=a中的第三个一维数组首地址,
此时可以通过 *(p+1)  p[2];这样的方式来访问其中的基础元素了。
    如果还要叠加更高维。应该也可以类推的。
    是这样的,不管几维。对于内存而言都是线性的。都是存了下一级数组的地址,直到最终访问到了值

c语言中存在多维数组,关系都如同二维数组对一维数组那样,都是存放了下一级数组的首地址。

7、指针和字符串

字符串操作的两种方式,

1.字符数组

char str[] = “abc”;

2.字符指针。

char *str = “abc”;

后者更方便。字符指针更方便。

#include<stdio.h>
int main(){
    char str[] = "Hello ";//字符数组
    char* str1 = "world!";//字符指针
    puts(str);
    puts(str1);
    return 0;
}
//结果
Hello
world!
    //字符串world!在内存中以字符串数组形式存储,语句char* str1 = "world!";
    //的含义是让指针str1指向字符串world!的首字符。存放首字符的地址。

char* str

char *str都是一样的, *位置没影响。

#include<stdio.h>
int main(){
    char a[1] = "AB";
    puts(a);
    return 0;
}
//输出是A?
?可能为其他,是随机的字符。
    数组太小,位置不够存不下了。
    char a[2] = "AB";
    puts(a);
修改,输出为AB??
    ??也是乱码,随机字符,他不知道你是否结束了字符串的输出。
    没有空间来存放字符串的休止符/0了。
    char a[3] = "AB";
这就是可以的。
    注意,字符串里的转义字符好像识别不了。视为字符串的一部分。
    除非是 printf("") 里面的输出框,就可以含转义字符。
    可以识别出来。
    char a[3] = "AB";
    printf("%c",a[1]);
输出结果为B
    char a[3] = "AB";
    printf("%d",a[2]);
输出结果为0;
8、二级指针和指针数组

指针可以指向任意类型的数据。

只要你有内存空间,万物皆可指,万物皆有地址。

包括指针类型,

指向指针的指针即是二级指针。

对应的,指针标识符也得用两个,标示他为二级指针。

int a,*p = &a;

int **pa = &p;

c语言中有多维数组,也有多级指针

多级数组,即n级数组的每个元素都是一个n-1级数组,直接引用初始元素时是n-1级数组名,即是其首元素地址。

多级指针,即n级指针存的是n-1级指针的地址。

只有最下一级初始级指向的内存中存了有效数据,(也可以是一个地址)

指向指针的指针,存放地址的地址。

要引用n级指针的对象,就要进行n次取值

2.指针数组

定义格式:dataType * pointerName [i];

指向 什么类型的指针变量 , 有多少个这样的指针成一组。

#include<stdio.h>
int main(){
    int a = 6;
    int *p1 = &a;
    int *P1[10];
    P1[0] = p1;
    printf("%d",*P1[0]);
    return 0;
}

当成普通的数组就行。只是存储的数据类型dataType是指针类型。

用法也是一致的。

只是所有这类指针知识都要分辨清楚地址和值的层级关系

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值