指针的指针、字符串和指针、数组指针(详)

article/2025/10/21 20:23:07

一、指针的指针

   指针的指针,即指针的地址

   定义了一个指针变量,指针变量本身占4个字节,指针变量也有地址编号

例:

   int a=0x12345678;

   假设a的地址为:0x0000 2000

   int *p;

   p=&a;

   则p中存放的是a的地址编号为0x0000 2000

   因为p也占4个字节,有自己的地址编号,即指针变量的地址,即指针的指针

   假设p的地址编号为0x0000 3000,这个地址是指针p的地址

   定义一个变量去存放p的地址编号,这个变量就是指针的指针

   int **q;

      q=&p;//q就保存了p的地址,也可以说q指向了p

  则q里存放的就是0x00003000

 p和q都是指针变量,都占4个字节,都是存放地址编号,只是类型不一样

#include<stdio.h>
void main()
{int a=0x12345678;int *p;int **q;int ***m;p=&a;printf("&a=%p\n",&a);printf("p=%p\n",p);q=&p;printf("&p=%p\n",&p);printf("q=%p\n",q);m=&q;printf("&q=%p\n",&q);printf("m=%p\n",m);printf("*p=%#x\n",*p);printf("**q=%#x\n",**q);printf("***m=%#x\n",***m);}
/* 
&a=0xbf9bde30
p=0xbf9bde30
&p=0xbf9bde34
q=0xbf9bde34
&q=0xbf9bde38
m=0xbf9bde38
*p=0x12345678
**q=0x12345678
***m=0x12345678*/

二、字符串和指针

字符串的概念:

            字符串就是’\0’结尾的若干的字符的集合:”hello”

            字符串的地址:是第一个字符的地址,字符串”hello”的地址,其实就是字符串中’h’的地址,即我们可以定义一个变量保存字符串的地址:char *s=”hello”;

字符串的存储形式:数组、文字常量区、堆

        1.字符串存在数组中

其实就是在内存(栈、静态全局区)中开辟了一段空间存放字符串。

char str[100]=”hello”;

定义了一个字符数组str,用来存放多个字符,并且用hello给str数组初始化,字符串”hello”,存放在str中

           注意:普通全局数组,内存分配在静态全局区

                 普通局部数组,内存分配在栈区

                 静态数组(静态全局数组、静态局部数组),内存分配在静态全局区

        2.字符串存放在文字常量区

   在文字常量区开辟了一段空间存放字符串,将字符串的首地址赋给指针变量

     char *str=”helloworld”;

定义了一个字符指针变量str,只能存放字符地址编号,

 “helloworld”这个字符串中的字符不是存放在str指针变量中

str只是存放了字符’h’的地址编号,“helloworld”存放在字符常量区

        3.字符串存放在堆区

   使用malloc等函数在堆区申请空间,将字符串拷贝到堆区

   char *str=(char *)malloc(100*sizeof(char));//动态申请了100个字节的存储空间

   首地址给str赋值

  strcpy(str,”helloworld”);//将字符串”helloworld”拷贝到str指向的内存中

字符串的可修改性:

     字符串的内容是否可以被修改,取决于字符串存放在哪里

        (1)存放在数组中的字符串的内容是可以修改的

                     char str[100]=”helloworld”;

                      str[0]=’y’;//正确的,可以修改

                     注:数组没有被const修饰(只要被const修饰的变量都不可以改)

        (2)文字常量区里面的内容是不可以修改的

                    char *str=”helloworld”;

   str[0]=’y’;//错误的,h是存放在文字常量区,不可修改

注:str指向文字常量区的时候,它所指向的内容不可以被修改

str是指针变量,可以指向别的地方,即可以给str重新赋值

        (3)堆区的内容是可以修改的

char *str=(char*)malloc(100);

strcpy(str,”helloworld”);

*str=’y’//正确,堆区的内容可以被修改

注意:str指向堆区的时候,它所指向的内容可以被修改

str是指针变量,可以指向别的地方,即可以给str重新赋值

总结:

        (1)字符串的内容是否可以被修改,取决于字符串存放在哪里

        (2)str指向文字常量区的时候,内存里面的内容不可以被修改

        (3)str指向数组(非const修饰)、堆区,它指向的内存里面的内容是可以被修改的

例1:
#include<stdio.h>
void main()
{char str[100]="hello world!!";printf("str=%s\n",str);str[0]='y';printf("str=%s\n",str);      //str=hello world!!//str=yello world!!
} 例2:
#include<stdio.h>
void main()
{char *str="hello world!!";printf("str=%s\n",str);//str=hello world!!*str='y';printf("str=%s\n",str); //Segmentation fault (core dumped)//改不了,出现段错误  
} 例3:
#include<stdio.h>
#include <stdlib.h>
#include <string.h>
void main()
{char *str=(char*)malloc(100);strcpy(str,"helloworld!!");//str=helloworld!!printf("str=%s\n",str); *str='y';printf("str=%s\n",str); //str=yelloworld!!
} 

字符串的初始化

        1.字符数组初始化:

 char buf[100]=”hello world”;

        2.指针指向文字常量区,初始化

 char *buf=”hello world”;

        3.指向堆区、堆区存放字符串

 不能一来就初始化,先给指针赋值,让指针指向堆区,再使用strcpy、scanf等方法把字符串拷贝到堆区

char *buf;

buf=(char *)malloc(100);

strcpy(buf,”helloworld”);

scanf(“%s”,buf);

字符串使用时赋值

        1.字符数组:使用scanf或者strcpy

   char buf[20]=”hello world”;

   buf=”hellokitty”;错误,因为字符数组的名字是个常量,不能用等号给常量赋值

   strcpy(buf,”hellokitty”);正确,数组的内容是可以被修改的

   scanf(“%s”,buf);正确,数组的内容是可以被修改的

        2.指针指向文字常量区

   char *buf=”hello world”;

   buf=”hellokitty”;正确,buf指向另外一个字符串

   strcpy(buf,”hellokitty”);错误,buf指向的文字常量区,内容是只读的,不可修改的,不能通过指针区修改文字常量区的内容

        3.指针指向堆区,堆区存放字符串

char *buf;

buf=(char *)malloc(100);

strcpy(buf,”helloworld”);

scanf(“%s”,buf);

字符串和指针总结

        (1)指针可以指向文字常量区

                1)指针指向的文字常量区的内容不可以修改

                2)指针的指向是可以改变的,重新赋值,让它指别的地方

 (2)指针指向堆区

        1)指针指向的堆区的内容可以修改

        2)指针的指向是可以改变的,重新赋值,让它指别的地方

 (3)指针指向数组(非const修饰)

char buf[20]=”hello world”;

char *str=buf;

  1. 可以修改buf数组的内容
  2. 可以通过str修改str指向的内存的内容,即buf的内容
  3. 不能给buf赋值,是常量
  4. 可以str赋值,让它指向别处

三、数组指针

        1.二位数组

   二维数组,有行有列。二维数组可以看成由多个一维数组构成的,是多个一维数组的集合,可以认为二维数组的每个元素一维数组

例:int a[3][5];

定义了一个3行5列的二维数组

可以认为二维数组a由3个一维数组构成,每个元素是一个一维数组(这个一维数组有五个元素)

思考:数组的名字是数组的首地址,即第0个元素的地址,是个常量,数组名字加1指向下一个元素。

二维数组a中,a+1指向下一个元素,即下一个一维数组,即下一行

      

一维数组a[10];---------->a是a[0]的地址即&a[0],a+1-------->&a[1];

      二维数组a[3][5];-------->a是a[0]的地址即&a[0],a+1--------->&a[1];

      a[3][5]={      

                {1,2,3,4,5},

                { 6,7,8,9,0},

                {5,4,3,2,1}

}

        2.数组指针的概念

   本身是一个指针,指向一个数组,加1跳一个数组,即下一个数组

        3.数组指针的定义方法

   指向的数组的类型  (* 指针变量名)   [元素个数]

     int ( * a)[5];//定义了一个数组指针变量a,a指向的是整型的有5个元素的数组a+1往下指5个整型,跳过一个有5个元素的一维数组

void main()
{int a[3][5];//定义了一个3行5列的数组int (*p)[5];//定义了一个数组指针变量p,P+1跳一个有5个元素的整型数组printf("a=%p\n",a);//第0行的地址printf("a+1=%p\n",a+1);//第1行的地址,a与a+1差20个字节p=a;printf("p=%p\n",p);printf("p+1=%p\n",p+1);printf("&a[0]=%p\n",&a[0]);//第0行的地址printf("&a[1]=%p\n",&a[1]);//第1行的地址printf("&a[0][0]=%p\n",&a[0][0]);//第0行首元素地址printf("&a[0][0]+1=%p\n",&a[0][0]+1);//第0行第2个元素地址
}

数组指针的用法1 
例子:
void fun(int(*p)[5])
{p[1][4]=10;
}
void main()
{int a[3][5]={ {1,2,3,4,5},{6,7,8,9,0},{0,0,0,0,0}   		   };//定义了一个3行5列的数组//fun()int i,j;fun(a);for(i=0;i<3;i++){for(j=0;j<5;j++){   printf("%d  ",a[i][j]);}printf("\n");} 
} 

         4.各种数组指针的定义

        (1)一维数组指针,加1后指向下个一维数组

     int (*p)[5];

配合每行有5个int型元素的二维数组来用

int a[3][5]

int b[4][5]

int c[5][5]

int d[6][5]

...

p=a;

p=b;

p=c;

p=d; 都是可以的

        (2)二维数组指针,加1后指向下个二维数组

int (*p)[4][5];

配合三维数组来用,三维数组中由若干个4行5列的二维数组构成的

int a[3][4][5]

int b[4][4][5]

int c[5][4][5]

int d[6][4][5]

...

p=a;

p=b;

p=c;

p=d; 都是可以的

void main()
{int a[3][4][5];printf("a=%p\n",a);printf("a+1=%p\n",a+1);//a和a+1地址编号相差80个字节int (*p)[4][5];         //验证了a+1跳一个4行5列的二维数组p=a;printf("p=%p\n",p);printf("p+1=%p\n",p+1);//p和p+1地址编号相差80个字节
} 

         (3)三维数组指针,加1后指向下一个三维数组

     int(*p)[4][5][6]

配合四维数组使用

int a[8][4][5][6]

多维数组指针以此类推......

        5.容易混淆的概念

指针数组:是个数组,有若干个相同类型的指针构成的集合

          int *p[10];

          数组p有10个int *类型的指针变量构成,分别是p[0]~p[9]

数组指针:是个指针,指向一个数组,加1跳一个数组

          int (*p)[10];

          p是个指针,是个数组指针,p加1指向下一个数组,跳10个整型

指针的指针

          int **p;//p是指针的指针

          int *q;

          p=&q;

        6.数组名字取地址,变成了数组指针

一维数组名字取地址,变成了一维数组指针,即加1跳一个一维数组

int a[10];

a+1跳一个整型元素,即a[1]的地址;

a和a+1相差了一个元素,4个字节

&a就变成了一维数组指针,是int(*p)[10]类型

(&a)+1和&a相差一个数组即10个元素即40个字节

拓展:&有升级功能  *有降级功能     &*相遇抵消

void main()
{int a[10];printf("a=%p\n",a);//a[0]的地址即&a[0],a=0xbfc00de8;printf("a+1=%p\n",a+1);//&a[1],a+1=0xbfc00dec与a相差4个字节printf("&a=%p\n",&a);//int(*p)[10]类型的,&a=0xbfc00de8printf("&a+1=%p\n",&a+1);//数组指针加1,跳一个数组,即跳10个元素//即40个字节 &a+1=0xbfc00e10
} 

总结:a是int *类型的指针,即a[0]的地址

     &a变成了数组指针int(*p)[10]类型,加1跳10个元素的一维数组

注意:

     在运行程序时,发现a和&a所代表的地址编号时一样的,即它们指向同一个存储单元,但是a和&a的指针类型不一样

void main()
{int a[4][5];printf("a=%p\n",a);//int (*p)[5]一维数组指针printf("a+1=%p\n",a+1);//与a相差多少个字节,5*4=20printf("&a=%p\n",&a);//int (*p)[4][5]  取地址后升级变成了二维数组指针printf("&a+1=%p\n",&a+1);//与&a相差多少个字节,4*5*4=80                       
} 
/*a=0xbfd624e0
a+1=0xbfd624f4
&a=0xbfd624e0
&a+1=0xbfd62530*/

         9.数组名字和指针变量的区别

   int a[5];

   int *p;

   p=a;

 相同点:a是数组的名字,是a[0]的地址,即p=a即p保存了a[0]的地址,即a和p都是指向的a[0],所以在引用数组元素的时候,a和p是等价的

引用数组元素回顾:

a[2]、*(a+2)、*(p+2)、p[2]都是对数组a中a[2]元素的引用

void main()
{int a[5]={0,1,2,3,4};int *p;p=a;printf("a[2]=%d\n",a[2]);printf("p[2]=%d\n",p[2]);printf("*(a+2)=%d\n",*(a+2));printf("*(p+2)=%d\n",*(p+2));                           
} 

不同点:(1)a是常量,p是变量

           可以用’=’给p赋值,不能用等号给a赋值

       (2)对a取地址,和对p取地址不一样

             因为a是数组的名字,对a取地址结果是数组指针int (*p)[5]类型

             &a+1-->5*4=20字节

             p是指针变量,所以对p取地址结果为指针的指针

             为int **类型

             &p+1---->4个字节

            往后指向一个int *类型的指针

void main()
{int a[5];int *p;p=a;printf("a=%p\n",a);//a是int *类型的指针,即a[0]的地址printf("&a=%p\n",&a);//&a变成了数组指针int(*p)[5]类型printf("&a+1=%p\n",&a+1);//与&a相差5*4=20printf("p=%p\n",p);//p是int *类型的指针printf("&p=%p\n",&p); //int **类型printf("&p+1=%p\n",&p+1); //与&p相差4个字节       
} 


http://chatgpt.dhexx.cn/article/EroG7Wwp.shtml

相关文章

指针的指针(简单易懂)

int a 12&#xff1b; int *b &a; 内存的分配如下 这时再来一个变量 c &b; 问题来了? c 是什么类型? b 是指向整型的指针 ,c 是指向整形指针的指针&#xff1f; 是的 c 是指向指针的指针 声明如下 int ** c; int a 12; int *b &a; int **c &b…

Nginx Rewrite规则详解

Nginx Rewrite 规则相关指令 相关指令有if,rewrite,set,return,break等&#xff0c;其中最关键的就是rewrite.一个简单的Nginx Rewrite规则语法如下&#xff1a;rewrite ^/b/(.*)\.html /play.php?video$1 break; 1.break指令 默认值&#xff1a;none ;使用环境&#xff1a…

nginx配置文件rewrite规则

nginx配置文件rewrite规则 文章目录 nginx配置文件rewrite规则[toc]ifRewite 规则介绍flag标志位配置rewrite规则last二次转发 if 语法&#xff1a;if (condition) {…} 应用场景&#xff1a; server段 location段 常见的condition 变量名&#xff08;变量值为空串&#xf…

nginx Rewrite 规则

一&#xff1a;nginx Rewrite 规则 1&#xff1a;rewrite的概念&#xff1a; Nginx Rewrite功能是使用nginx提供的全局变量或自己设置的变量&#xff0c;结合正则表达式和标志位实现URL重写以及重定向功能。Rewrite指令只能放在server {}&#xff0c;location {}&#xff0c;…

Nginx高级之Rewrite规则

进阶阶段的回顾: Nginx进阶之静态Web资源服务 Nginx进阶之代理服务 Nginx进阶之负载均衡服务 Nginx进阶之缓存服务和动静分离 作用及应用场景 作用: 实现对URL的重写以及对匹配(正则表达式)的url的重定向 场景: 1. URL访问跳转, 支持开发设计 ① 页面跳转 ② 兼容…

Nginx配置请求转发location及rewrite规则

location / {# 精确匹配 / &#xff0c;主机名后面不能带任何字符串[ configuration A ] }location / {# 因为所有的地址都以 / 开头&#xff0c;所以这条规则将匹配到所有请求# 但是正则和最长字符串会优先匹配[ configuration B ] }location /documents/ {# 匹配任何以 …

Rewrite规则简介

Rewirte主要的功能就是实现URL的跳转&#xff0c;它的正则表达式是基于Perl语言。可基于服务器级的(httpd.conf)和目录级的(.htaccess)两种方式。如果要想用到rewrite模块&#xff0c;必须先安装或加载rewrite模块。方法有两种一种是编译apache的时候就直接安装rewrite模块&…

rewrite详解

rewrite模块 URI跟URL介绍 什么是uri&#xff1f;统一标识符&#xff0c;拿www.abc.com/aw/wd/举例&#xff0c;那么rui就是/aw/wd/这部分数据(也有可能是图片&#xff0c;html网页,如果是伪静态的话,那就得看配置是啥玩意了 什么是url? 统一定位符&#xff…

Nginx基础——Rewrite规则

点击上方“芋道源码”&#xff0c;选择“置顶公众号” 技术文章第一时间送达&#xff01; 源码精品专栏 精尽 Dubbo 原理与源码专栏( 已经完成 69 篇&#xff0c;预计总共 75 篇 )中文详细注释的开源项目Java 并发源码合集RocketMQ 源码合集Sharding-JDBC 源码解析合集Spring …

F280049C Crossbar X-BAR

文章目录 X-BAR9.1 输入X-BAR9.2 ePWM、CLB和GPIO输出X-BAR9.2.1 ePWM X-BAR9.2.1.1 ePWM X-BAR架构 9.2.2 CLB X-BAR9.2.2.1 CLB X-BAR架构 9.2.3 GPIO输出X-BAR9.2.3.1 GPIO输出X-BAR架构9.2.4 X-BAR标志 总结 X-BAR 交叉开关&#xff08;在本章中称为X-BAR&#xff09;提供…

BCGControlBar Pro 31.2 正式版-Key

什么是 MFC 的 BCGControlBar Pro&#xff1f; BCGControlBar&#xff08;“Business Components Gallery ControlBar”&#xff09;是一个 MFC 扩展库&#xff0c;企鹅180846090允许您创建具有完全自定义选项&#xff08;功能区、可自定义工具栏、菜单等&#xff09;和一组丰富…

BCGControlBar Library for .NET 7.1.1 Crack

什么是 BCGControlBar Library for .NET&#xff1f; BCGControlBar Library for .NET 是 100% 托管代码工具包&#xff0c;用 C/CLI 编写&#xff0c;面向 Microsoft .NET Framework 2.0 或更高版本。该库包含许多高度可定制、完全可设计的组件&#xff0c;使您能够创建最复杂…

BCGControlBar v12的向导使用图解

BCGControlBar专业版是MFC的一个扩展库&#xff0c;您可以用来构建类似于Microsoft Office 2000/XP/2003/2007/2010、Microsoft Visual Studio&#xff08;打印、用户定制工具栏、菜单等&#xff09;和其他一些知名产品的高级用户界面。 首先从网上下载BCGControlBar v12资源 &…

MFC界面控件BCGControlBar v33.4 - 日历、属性网格组件升级

BCGControlBar库拥有500多个经过全面设计、测试和充分记录的MFC扩展类。 我们的组件可以轻松地集成到您的应用程序中&#xff0c;并为您节省数百个开发和调试时间。 BCGControlBar专业版和BCGSuite for MFC v33.4已正式发布了&#xff0c;该版本包含了对Windows 11 Mica materi…

BCG学习(一)——BCGControlBar安装与配置

最近工作中需要用到BCG相关的知识&#xff0c;趁着全民防疫、居家隔离这段时间正好学习一下&#xff0c;作此笔记&#xff0c;记录学习过程和心得体会。话不多说&#xff0c;开整&#xff01; 简介 下载、安装与配置 例程编译与运行 简介 BCG是MFC的一个扩展库&#xff0c;可以…

MFC扩展库BCGControlBar Pro v33.5新版亮点 - 控件、脚本管理增强

BCGControlBar库拥有500多个经过全面设计、测试和充分记录的MFC扩展类。 我们的组件可以轻松地集成到您的应用程序中&#xff0c;并为您节省数百个开发和调试时间。 BCGControlBar专业版 v33.5已正式发布了&#xff0c;此版本包含了Ribbon&#xff08;功能区&#xff09;自定义…

MFC界面控件BCGControlBar v33.4 - 支持Win 11 Mica material主题

BCGControlBar库拥有500多个经过全面设计、测试和充分记录的MFC扩展类。 我们的组件可以轻松地集成到您的应用程序中&#xff0c;并为您节省数百个开发和调试时间。 BCGControlBar专业版和BCGSuite for MFC v33.4已正式发布了&#xff0c;该版本包含了对Windows 11 Mica materi…

MFC界面控件BCGControlBar v33.4 - 各子控件全面升级

BCGControlBar库拥有500多个经过全面设计、测试和充分记录的MFC扩展类。 我们的组件可以轻松地集成到您的应用程序中&#xff0c;并为您节省数百个开发和调试时间。 BCGControlBar专业版和BCGSuite for MFC v33.4已正式发布了&#xff0c;该版本包含了对Windows 11 Mica materi…

MFC界面控件BCGControlBar v33.3 - 升级Ribbon Bar自定义功能

BCGControlBar库拥有500多个经过全面设计、测试和充分记录的MFC扩展类。 我们的组件可以轻松地集成到您的应用程序中&#xff0c;并为您节省数百个开发和调试时间。 该版本包含了增强的Ribbon自定义、新的日期/时间数字指示器、带有文本对齐的组控件、多行支持以及其他一些新功…

MFC界面控件BCGControlBar v33.3 - 可视化管理器和主题更新

BCGControlBar库拥有500多个经过全面设计、测试和充分记录的MFC扩展类。 我们的组件可以轻松地集成到您的应用程序中&#xff0c;并为您节省数百个开发和调试时间。 BCGControlBar专业版和BCGSuite for MFC v33.3已正式发布了&#xff0c;该版本包含了增强的Ribbon自定义、新的…