当前位置: 首页 > news >正文

二级指针的思考

最近在群里出现了一道面试提,看着打架讨论挺热烈就开始思考一下这个问题。下边先把题贴出来:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void Getmemery(char *p)
{
    p=(char *)malloc(100);
}

void main()
{
    char *str=NULL;

    Getmemery(str);
    strcpy(str,"hello world");
    printf("%s",str);
    free(str);
}

这就是这个道题了,这里边是有个错误的。不知道看出来没有。

 

其实要更清楚的搞懂这个问题,我们首先需要了解一个知识点。就是    堆、栈、自由存储区、全局/静态存储区和常量存储区。

 

我们在弄懂这个问题之后 看下边一个小例子:

 

1 int main () {
2     char* p= "hello";
3     *p = "world";
4     printf("%s",p);
5     return 0;
6 } 

你编译运行之后就会发现错误,这个错误就在于你试图去改变 常量存储区 里边的内容。那我们需要修改的话怎么办?

int main () {
    char* p= "hello";
    char *str = "world";
    p = str;   //改变地址的指向 
    printf("%s",p);
    return 0;
} 

改变它地址的指向,这个就是方法,你如果非要改变常量存储内内容也不是没有方法,这个不属于考虑范围。

 


 

上边的问题理解清楚了我们就这是开始进入下一个话题

指针数组

定义: 指针数组中每一个元素都存放一个地址。都当于一个指针变量。 

示例: int* p[4];   char* type = {"C", "C++", "JAVA", "C#", "Ruby"};

 

 1 int main() {
 2 
 3     char* type[] = { "C", "C++", "JAVA", "C#", "Ruby" };
 4     int a[] = { 1,2,3,4,5 };
 5     int *num[5];
 6 
 7     for (int n = 0; n < 5; n++) {
 8         printf("%s  ", type[n]);
 9     }
10 
11     for (int i = 0; i < 5; i++) {
12         num[i] = &a[i];
13     }
14     printf("\n");
15 
16     for (int j = 0; j < 5; j++) {
17         printf("%p  ", num[j]);
18     }
19     printf("\n");
20     for (int k = 0; k < 5; k++) {
21         printf("%d  ", *num[k]);
22     }
23     printf("\n");
24 
25     return 0;
26 }

           

在这个例子中我们应该能分清楚 指针数组里边到底存的是什么。 换句话说  int *num = {&a[0], &a[1], &a[2], &a[3], &a[4], &a[5]}

 


 

基本了解指针数组之后,我们做点小的测试看看对于指针的概念怎么样?

 1 void change(char *p);
 2 int main() {
 3     char old[] = { 1,3 ,5 }; 
 4     printf("&old[0] = %p\n", &old[0]);
 5     printf("&old[1] = %p\n", &old[1]);
 6     printf("&old[2] = %p\n", &old[2]);
 7     char *p = old;     // [1] 
 8     change(p);
 9     for (int i = 0; i < 3;i++,p++) {
10         printf("%d  ", *p);
11     }
12     printf("\n");
13     return 0;
14 }
15 void change(char *p) {
16     char news[] = {2,4,6};  //[2]
17     p = news;               //[3]
18 }

 

 

 

结果是 还是  1, 3, 5。 可能某些就会产生疑问了,怎么没有改变。不是使用的地址传递。 我们解释一下这个问题。C不同于C++,参数是通过值传递或者地址传递的,而不是通过引用。也就是说,实际参数  先自己copy一份,然后传递给形式参数 *P接收

 

 

在执行完第一步之后 也就是 7行, P指向old 的都地址。 执行第二步,对值进行拷贝出现P_COPY 指向old,添加新的数组。 第三步,P_COPY 指向new  数组的首地址。 这个过程是P_COPY 指向new   与main()函数里边的 P 没有半毛钱关系。  -- 听到这里可能明白,也可能有点晕,我们下边继续了解。怎么让两个真交换呢?

void change(char **p);
int main() {
    char old[] = { 1,3 ,5 }; 
    printf("&old[0] = %p\n", &old[0]);
    printf("&old[1] = %p\n", &old[1]);
    printf("&old[2] = %p\n", &old[2]);
    char *p = old;            // [1] 
    change(&p);             // [2]
    for (int i = 0; i < 3;i++,p++) {
        printf("%d  ", *p);
    }
    printf("\n");
    return 0;
}
void change(char **p) {
    char *pt = NULL;
    static char news[] = {2,4,6};     //[3]  //特别注意哈 static
    *p = news;                        //[4]
}

 在进行完第二步进行数值的拷贝, P_COPY  = &P;   意味着 *P_COPY 指向 P所在空间, 改变*P_COPY 里边的值 会改变P 里边的值,即最改变最终指向。这里边和上题的区别是我们是用到P这个指针变量的地址,用 P_COPY存储, 使得P_COPY 和 P 产生了关联。  这是和上题最大的区别。

 

下边还是一个交换的例子自己体会一下。 

 1 void swap(char **str, char **dest);
 2 int main() {
 3     char *A = "abcd", *B = "efgh";
 4     printf("A = %p, B = %p\n", A, B);
 5     printf("&A = %p, &B = %p\n", &A, &B);
 6     swap(&A, &B);
 7 
 8     printf("交换后: A = %p, B = %p\n", A, B);
 9     printf("交换后: A = %s ,B = %s", A, B);
10     return 0;
11 }
12 void swap(char **str, char **dest) {
13     char *p = NULL;
14     printf("str = %p, dest = %p\n", str, dest);
15     p = *str;
16     *str = *dest;
17     *dest = p;
18 }

这是一个交换 的实例, 在C语言中有值传递地址传递, 但是没有引用(c++)中概念。

 

转载于:https://www.cnblogs.com/causal360/p/4796810.html

相关文章:

  • JSP隐含对象 request(2)
  • JSP实现网页计算器
  • MySQL汇总数据
  • 如何配置Tomcat的热启动
  • vncserver
  • 项目路径配置[pageContext.request.contextPath]和c标签
  • JSP隐含对象response实现文件下载的两种方式
  • JSP动态生成验证码
  • 报表性能优化方案之多种报表服务器内存修改方法
  • JSP用过滤器解决request中文乱码问题
  • Advanced R之编程风格
  • SpringMVC实现一个controller写多个方法
  • 枚举类型的抽象方法定义实现
  • 大数据笔记07:大数据之Hadoop的HDFS(特点)
  • 【数据库学习笔记】(1)JDBC驱动程序的分类
  • 【剑指offer】让抽象问题具体化
  • Angular数据绑定机制
  • gcc介绍及安装
  • JS正则表达式精简教程(JavaScript RegExp 对象)
  • Mac 鼠须管 Rime 输入法 安装五笔输入法 教程
  • maven工程打包jar以及java jar命令的classpath使用
  • spring学习第二天
  • 从输入URL到页面加载发生了什么
  • 计算机在识别图像时“看到”了什么?
  • 嵌入式文件系统
  • 再谈express与koa的对比
  • [Shell 脚本] 备份网站文件至OSS服务(纯shell脚本无sdk) ...
  • 阿里云服务器购买完整流程
  • ​520就是要宠粉,你的心头书我买单
  • #常见电池型号介绍 常见电池尺寸是多少【详解】
  • #图像处理
  • (1)SpringCloud 整合Python
  • (C#)获取字符编码的类
  • (附源码)spring boot车辆管理系统 毕业设计 031034
  • (附源码)springboot美食分享系统 毕业设计 612231
  • (附源码)springboot优课在线教学系统 毕业设计 081251
  • (附源码)ssm跨平台教学系统 毕业设计 280843
  • (六)激光线扫描-三维重建
  • (七)微服务分布式云架构spring cloud - common-service 项目构建过程
  • (十五)Flask覆写wsgi_app函数实现自定义中间件
  • (转载)从 Java 代码到 Java 堆
  • .htaccess配置常用技巧
  • .NET 5种线程安全集合
  • .net core 3.0 linux,.NET Core 3.0 的新增功能
  • .net6+aspose.words导出word并转pdf
  • .NET程序员迈向卓越的必由之路
  • .net和php怎么连接,php和apache之间如何连接
  • @EnableConfigurationProperties注解使用
  • [AutoSar]BSW_Memory_Stack_004 创建一个简单NV block并调试
  • [BPU部署教程] 教你搞定YOLOV5部署 (版本: 6.2)
  • [bzoj1006]: [HNOI2008]神奇的国度(最大势算法)
  • [delphi]保证程序只运行一个实例
  • [hdu4622 Reincarnation]后缀数组
  • [HNOI2015]实验比较
  • [java基础揉碎]关系运算符(比较运算符)逻辑运算符赋值运算符三元运算符运算符的优先级