首页 > 其他分享 >c语言笔记4

c语言笔记4

时间:2023-08-19 15:12:16浏览次数:30  
标签:语言 int 笔记 char 地址 数组 变量 指针

c语言笔记4(指针)

1. 指针的应用

1.1 内存空间

32位机: 一次处理数据的大小 4B(字节)

64位机: 一次处理数据的大小 8B (字节)

计算处理数据的最小单位是 1B(字节), 计算存储数据的最小单位 二进制的1b(位)

一个程序启动后的进程分区: 栈、堆、全局区、常量区、代码区

内存寻址: (32位)最大范围(4B) 0xff ff ff ff, 最小 0x00 00 00 00

1.2 指针的概念

什么是指针: 是内存的地址编号(地址)。

指针是地址编号的类型。

1.指针变量:是个变量,即这个变量用来存放一个地址编号。

2.指针变量存放的是地址编号,一个地址编号是4B(32位机)或8B(64位机)。

3.指针变量也具有数据类型, 如 int * 表示int类型的指针。

4.定义指针变量

数据类型 *指针名;

5.*&的关系

在定义指针时,*表示与前面的数据类型组合成`数据类型*`,*后变量名指针名。在获取指针的值(从地址编号的空间读取值) *指针名

&求变量的地址,将变量转化为它本身数据类型的*表示

小结:

1)无论什么类型的地址,都是存储单元的编号,在32位平台下都是4个字节,即任何类型的指针变量都是4个字节大小:在64位平台下都是8个字节。
2)对应类型的指针变量,只能存放对应类型的变量的地址
3)字符变量char ch;ch占1个字节,它有一个地址编号,这个地址编号就是ch的地址
4)指正变量的数据类型的作用:一次性读取数据的宽度及跨度

【注】小字节的变量的地址不能给大字节类型的指针,避免读取脏数据

1.3 指针的分类

1. 普通数据类型的指针
 char,short,int,long,float,double
2. 函数指针:指向函数的指针
  应用场景:函数回调(int *(函数指针名)(形参类型列表))
3. 结构体指针:struct x*名称
4. 指针的指针:char|short|int... **指针名
5. 数组的指针:char|short|int... (*指针名)[]

2. 指针与变量的关系

2.1 指针的赋值

变量操作:1)先定义变量 2)初始化 3)修改变量的值

指针变量获取到某一个变量的地址时,则指针指向这个变量地址,可以通过指针操作变量

int *p=NULL;   //未初始化,不能使用。【建议】定义指针时,没有初始化值时,赋值为NULL
// 将p指针指向a;
p = &a;
// 指针初始化指向变量
int *q=&b;

2.2 指针的取值

对指针变量取值时,即对指针地址取值,也就是对指向的变量取值

【注意】*+指针变量就相当于指针指向的变量

// a += 50;
*p += 50
// *q == b
int ret = *p + *q;  //a + b

void*表示为任意类型(自由类型,无类型)

3. 指针与数组元素的关系

3.1 指针指向数组元素的地址

数组中的元素也是变量,它具有自己的内存地址编号,这个编号可以辅助指针,指针变量的类型同元素(变量)的数据类型,即同数组的数据类型保持一致

3.2 数组元素的引用

数组名[下标] 访问数组元素

如果一个指针指向数组的第一个元素的内存位置,也可以通过指针名[下标]访问指定位置的元素。

【注意】c语言中,数组名是一个常量,存放数组元素的首地址(第一个元素的内存地址)

3.3 指针的运算

1)指针+数值

结果还是一个地址

2)指针比较

两个相同类型的指针可以进行比较,两个地址(指针)比较时,比较的还是地址。

3)指针相差

两个相同类型的地址相差,地址的8字节十六进制数相差之后得到的数值 除以数据类型的字节大小

4)指针赋值指针

将一个指针地址赋值给另一个相同类型的指正

int arr[10]; int*p=arr; int *q=p;

4. 指针数组

指针可以定义一个数组,数组中有若干个相同类型的指针变量,这个数组被称为指针数组。指针数组本质上也是一个数组,只不过存储的元素是指针变量。

4.1 指针数组的定义

语法:

数据类型 * 数组名[元素个数];

4.2 指针数组的分类

1)字符指针数组 char *names[5];
2)整数型指针数组 short *xxx[N];
                int *xxx[N];
                long *xxx[N];
3)浮点型指针数组  float * xxx[N];
                double *xxx[N];
4)结构体指针数组  struct Stu *xxx[N];
5)函数指针数组    void (*xxx)(int,int)[]

5. 指针的指针

即指针的地址

p==&a
*p==a
q==&p
*q==p==&a
**q=*p=a

6. 字符串与指针

字符串

以'\0'结尾的多个字符组成的集合

字符串的存储形式:

字符数组:  char name[128]="xxx";
字符串指针: char *name="xxx";
堆区:     char *name = (char*)malloc(128)//动态从堆中分配空间
          strcpy(name,"xxxx");//从string.h头文件中的复制字符串的函数

【注】p[0]='x'//不能修改的,常量区

7. 数组指针

7.1 二维数组名运算

数组名代表是第0行的地址,即是一个二维数组的首地址

数组名+1,代表取下一行的地址编号,跨了一整行(一行有N列,即跨了N列)

7.2 二维数组的指针定义

定义指针,指向一个二维数组,即为二维数组的指针

语法

数组元素的数据类型 (*指针名)[列数];

如:通过指针方式遍历一个二维数组

int m[3][5];
int (*p)[5]=m;
for(int i=0;i<3;i++){
    for(int j=0;j<5;j++)
    {
        printf("第%d行第%d列,输入数据:",i,j);
        scanf("%d",&m[i][j]);//m[i]是地址,指针[]取元素
    }
}
for(int i=0;i<3;i++){
    for(int j=0;j<5;j++){
        printf("第%d行%d列 %d\n",i,j,m[i][j]);
    }
}

7.3 不同数组指针

一维数组指针,指向是二维数组,+1即一次跨N列(一行)

int arr[行数][列数];
int (*p)[列数] = arr;

二维数组指针,指向是三维数组,+1即一次跨一个二维数组

int arr[N][行数][列数];
int (*p)[行数][列数] = arr;

三维数组指针,指向是四维数组,+1即一次跨一个三维数组,依次类推...

【注】

指针数组:本质上是数组,存储的是指针
数组指针:本质上是指针,指向是数组
指针的指针:本质上是指针,指针指向是地址(指针)。

7.4 数组名取地址

如果一个数组名取地址,即获取的是地址的地址,转成为数组指针

【注】数组名与指针变量的区别

  1. 共同点:

数组名也是一个第一个元素的地址,指向数组元素地址的指针同组名相同的。

2.不同点:

1)数组名取地址变量数组指针,&a == int (*)[10];
2)指针变量取地址,还是指针,&p == int **;

7.5 多维数组指针的转换

数组名前加*不是取值,是从行地址转化为行中的第一列的地址

1)一维数组指针名+[],取某行的首列地址,等价于*(指针+i)

2)*(指正变量 + 行号)获取此行的首列地址

8. 函数与指针

函数传递参数的形式:1)值 2)地址

地址作为实参传入到函数中,函数需要指针来接收

8.1 变量地址作为实参

调用函数时,求变量的地址,&变量名。

【提示】在函数内部可以通过地址修改变量值

void ch(int *p)
{
    *p += 10;
}
int main(int argc,char const *argv[])
{
    int x = 10;
    ch(&x);
    printf("x=%d\n",x);
    return 0;
}

8.2 字符指针作为实参

字符指针作为实参,将指针指向常量区的内容传递函数。函数内部修改指针内容时,不会影响函数外部的值。

void ch(char *p)
{
    printf("ch %s,p=%p\n",p,p);
    p="xx";
    printf("ch %s,p=%p\n",p,p);
    //*p = 'x';//p指向常量区,常量区的内容不能更改
}
int main()
{
    char *name = "xxx";
    ch(name);//值传递
    printf("%s,p=%p\n",name,name);
    return 0;
}

8.3 字符指针地址作为实参

将字符指针的指针作为函数的实参,函数的形参使用**q(指针的指针),函数内部可以修改字符指针地址,即可以函数外部的结果

void ch(char **p)
{
    *p = "xx";
    printf("ch *p=%s,p=%p\n",*p,p);
}
int main()
{
    char *name = "xxx";
    ch(&name);//属于地址传递
    printf("%s,&name=%p\n",name,&name);
    return 0;
}

要想改变主调函数中变量的值,必须传变量的地址,而且还得通过*+地址去赋值。无论变量是什么类型

8.4 数组作为实参

数组名是地址(指针)作为函数的实参,属于地址传递

函数的形参定义:

1)接收一维数组的地址,按int *p格式定义

2)接收二维数组的地址,按一维数组的指针来定义,int (*p)[列数]

3)接收三维数组的地址,按二维数组的指定来定义

.......

设计一个函数,接收一个二维数组,指定函数、列数、查找的值和替换新值,实现所有查找值的更新或替换
    
void findPeplace(int rows;int cols,int (*p)[cols],int oval,int nval)
{
    for(int i = 0;i < rows;i++)
    {
        for(int j = 0;j < cols;j++)
        {
            if(*(*(p+i)+j)==oval)
            {
                *(*(p+i)+j)=nval;
            }
        }
    }
}

8.5 字符指针数组作为实参

字符指针数组作为实参时,函数的形参的写法:1)char*q[] 2)char **q

字符指针数组,数组中的每一个元素都是一个字符指针的地址

数组名,默认指向是每一个元素的地址

void show(char **q,int size)//char *q[]==char**q
{
    for(int i=0;i<size;i++)
    {
        printf("%s\n",*(q+i));//q+i下一个字符指针地址
    }
}
int main()
{
    char *names[3] = {"x","y","z"};
    show(names,3);
    return 0;
}

8.6 指针作为函数的返回值

例:

void *rand_arr(int size,int min,int max)
{
    int rand_nums[size];//本地变量,局部变量
    srand(time(NULL));//设置随机数的种子
    for(int i = 0;i<size;i++)
    {
        rand_nums[i] = read()%(max-min+1)+min;
    }
    return rand_nums;
}
int main()
{
    int *nums = (int *)rand_arr(10,50,100);
    for(int i = 0;i<10;i++)
    {
        printf("%d",*(nums +i));
    }
    printf("\n");
    return 0;
}

这个代码的问题是:函数返回局部变量的地址,函数弹栈(结束),返回的地址空间会自动释放,在函数之外使用时是无效的。

解决办法:将返回地址的变量类型修改为静态局部变量即可。

static int rand_nums[10];

9. 经常容易混淆的指针

数组相关:

int *a[10];   指针数组,本质上是数组,存放的元素是指针
int (*a)[10];  数组指针,本质上是指针,指向的二维数组中第一个元素

函数相关:

int *f(void); //void表示函数无任何的形参
          函数返回值是 int* 类型的指针
int (*f)(void);
          函数指针,指向一个函数的地址

特殊指针:

void *:空类型的指针,可以强转成任意类型。
        char *|short *|int *...
NULL:空,一般作为指针初始化的值,即代表空指针。默认值为0x0。   

标签:语言,int,笔记,char,地址,数组,变量,指针
From: https://www.cnblogs.com/dijun666/p/17642473.html

相关文章

  • 【刷题笔记】25. Reverse Nodes in k-Group
    题目Givenalinkedlist,reversethenodesofalinkedlistkatatimeandreturnitsmodifiedlist.kisapositiveintegerandislessthanorequaltothelengthofthelinkedlist.Ifthenumberofnodesisnotamultipleofkthenleft-outnodesint......
  • openGauss学习笔记-44 openGauss 高级数据管理-存储过程
    openGauss学习笔记-44openGauss高级数据管理-存储过程存储过程是能够完成特定功能的SQL语句集。用户可以进行反复调用,从而减少SQL语句的重复编写数量,提高工作效率。44.1语法格式创建存储过程CREATEPROCEDUREprocedure_name[({[argname][argmode]argtype[......
  • AI-1515. 自然语言处理:应用
    15.4. 自然语言推断与数据集自然语言推断(naturallanguageinference)主要研究 假设(hypothesis)是否可以从前提(premise)中推断出来,其中两者都是文本序列。换言之,自然语言推断决定了一对文本序列之间的逻辑关系。这类关系通常分为三种类型:蕴涵(entailment):假设可以从前提中推断......
  • Redis分布式锁笔记
    1redis分布式锁实现原理所谓分布式锁,应当基本如下几项核心性质:• 独占性:对于同一把锁,在同一时刻只能被一个取锁方占有,这是锁最基础的一项特征• 健壮性:即不能产生死锁(deadlock).假如某个占有锁的使用方因为宕机而无法主动执行解锁动作,锁也应该能够被正常传承下去,被其......
  • 云原生之使用Docker部署开源Leanote蚂蚁笔记
    (云原生之使用Docker部署开源Leanote蚂蚁笔记)一、Leanote蚂蚁笔记介绍1.Leanote简介Leanote蚂蚁笔记是一款云笔记工具,蚂蚁笔记(又名LeaNote)就是一款国产开源的私有云笔记软件。它支持普通格式笔记、Markdown语法、专业数学公式编辑、和思维脑图,常见的笔记相关功能它都拥有,同时......
  • AI百度文心一言大语言模型接入使用(中国版ChatGPT)
    一、百度文心一言API基于百度文心一言语言大模型的智能文本对话AI机器人API,支持聊天对话、行业咨询、语言学习、代码编写等功能.二、使用步骤1、接口重要提示:建议使用https协议,当https协议无法使用时再尝试使用http协议请求方式:POSThttps://luckycola.com.cn/ai/openwx......
  • springcloud学习笔记
    springcloud2020开始取消英国地铁命名方式。 注册中心、配置中心:nacos服务调用:feign服务熔断:sentinel网关:gateway链路:sleuth ......
  • R语言的数据结构与转换
    文章和代码已经归档至【Github仓库:<https://github.com/timerring/dive-into-AI>】或者公众号【AIShareLab】回复R语言也可获取。任何数据分析的第一步都是按照所需要的格式创建数据集。在R中,这个任务包括两个步骤:首先选择一种数据结构来存储数据,然后将数据输入或者导入这个数......
  • 笔记2
    非0真!!!-0假C语言是一门结构化的程序设计语言1.顺序结构2.选择结构3.循环结构分支语句1.语句,由分号隔开叫一个语句。inta=0; 一个语句。;单独一个分号 ,是语句,是个空语句。if语句if(表达式)------表达式为真,执行语句 语句;//多分支if(表达式1) 语句1;elseif(表达式2) ......
  • GO语言八股
    string和[]byte转换会发生内存拷贝吗?在Go语言中,string和[]byte之间的转换会导致内存拷贝。这是因为string类型是不可变的,而[]byte类型是可变的。当进行string到[]byte的转换时,需要创建一个新的[]byte切片,并将string的数据复制到新的切片中。同样地,当进行[]byte到string的转换时,也......