对于语句  char *a="hello";

对于这个声明方式,会造成的误解是:声明了一个字符指针(它会指向一个位置),将“字符串”赋值给 指针表达式"*a"所指向的地址。但正解是:声明了一个字符指针后,并用字符串常量的第一个字符的地址赋值给指针变量a。
即正确顺序是:

  • 1.分配内存给字符指针;
  • 2.分配内存给字符串;
  • 3.将字符串首地址赋值给字符指针;

这里有两点需要考虑清楚的地方:

*a只是指向一个字符。举例如下:

#include <iostream>
#include <string>
using namespace std; int main() { char *a ="abcdefg";
cout << "输出字符: " << *a << endl;
cout << "第二次输出字符: " << *(a+) << endl;
cout << "输出字符串: " << a << endl; return ;
}

结果如下

输出字符: a
第二次输出字符: b
输出字符串: abcdefg

 若字符串常量出现在在表达式中,代表的值为该字符串常量的第一个字符的地址。所以”hello”仅仅代表的是其地址。 
原声明方式相当于以下声明方式:

char *a;
a="hello";/*这里字符串"hello"仅仅代表其第一个字符的地址*/

1.但还是不明白为什么字符串可以赋值给字符指针变量

char *p,a='';
p=&a; //显然是正确的,
p="abcd"; //但为什么也可以这样赋值??

双引号做了3件事:  
1.申请了空间(在常量区),存放了字符串 
2. 在字符串尾加上了'/0'    
3.返回地址
返回的地址,赋值给了指针变量p    

2.以char *p = “hello”为例,把p换成数组,然后再赋值就不行了

字符串常量"hello"出现在一个表达式中时,"hello"表达式使用的值就是这些字符所存储的地址(在常量区),而不是这些字符本身。

所以,可以把字符串赋值给指向字符的指针p,而不能把字符串赋值给一个字符数组。

char a[10] = “hello”; //这样可以,这种情况是c语言初始化所支持的

如果写成char a[10]

然后 a = “hello” 这样就错误了。

因为同样是a数组,char a[10] = “hello”;这种是数组的初始化,和a[0] = ‘h’ a[1] = ‘e’…是一个道理

但是换成char a [10],然后a = “hello”是不行的,“hello”赋值的值是一个地址,而a虽然也有地址,与指针不同,指针的值是地址,而数组的值虽然同为地址,却是一个常量,不能给常量赋值

代码测试

#include <iostream>
#include <string> using namespace std; int main() {
char *p = "hello";
cout << p << endl;
char a[];
a = "hello";
return ;
}

报错 error C3863: array type 'char [10]' is not assignable

而修改后,正常运行

#include <iostream>
#include <string> using namespace std; int main() {
char *p = "hello";
cout << p << endl;
char a[] = "hello"; //数组初始化
return ;
}

字符数组,字符指针,字符串常量 知识回顾

1.以字符串形式出现的,编译器都会为该字符串自动添加一个0作为结束符,如在代码中写 "abc",那么编译器帮你存储的是"abc\0"

2."abc"是常量吗?
1.当作为字符数组初始值的时候,"abc"不是常量

char str[] = "abc";

因为定义的是一个字符数组,所以就相当于定义了一些空间来存放"abc",而又因为字符数组就是把字符一个一个地存放的,所以编译器把这个语句解析为

char str[] = {'a','b','c','\0'};

2.当赋值给 字符指针变量的时候:如char* ptr = "abc" 此时是常量

char* ptr = "abc" //规范不允许这种c的写法

规范写法

const char* ptr = "abc";

因为定义的是一个普通字符指针,并没有定义空间来存放"abc",所以编译器得帮我们 找地方来放"abc",显然,把这里的"abc"当成常量并把它放到程序的常量区是编译器 最合适的选择。简言之,如果char* ptr = "abc";写在函数体内,那么虽然这里的"abc\0"被 放在常量区中,但是ptr本身只是一个普通的指针变量,所以ptr(指针)是被放在栈上的, 只不过是它所指向的东西(值)被放在常量区罢了

3.数组的类型是由该数组所存放的东西的类型以及数组本身的大小决定的

如char s1[3]和char s2[4],s1的类型就是char[3],s2的类型就是char[4], 也就是说尽管s1和s2都是字符数组,但两者的类型却是不同的

4.字符串常量的类型可以理解为相应字符常量数组的类型

如"abcdef"的类型可以看成是const char[7]

5.sizeof是用来求类型的字节数的。

如int a;那么无论sizeof(int)或者是sizeof(a)都 是等于4,因为sizeof(a)其实就是sizeof(type of a)

6.对于函数参数列表中的以数组类型书写的形式参数,编译器把其解释为普通 的指针类型

void func(char sa[],int ia[],char *p) 

则sa的类型为char*
ia的类型为int*
p的类型为char*

7.根据上面的总结,来实战一下

1)对于char str[] = "abcdef";就有sizeof(str) == 7,因为str的类型是char[7] str本身可变

2)也有sizeof("abcdef") == 7,因为"abcdef"的类型是const char[7] 字符串常量

3)对于char *ptr = "abcdef";就有sizeof(ptr) == 4,因为ptr的类型是char*,即


#include <iostream>
#include <string>
using namespace std;
int main() {

    char *p = "hello";
cout << sizeof(p) << endl; //
cout << sizeof(char *) << endl; //
return ;
}

4)对于char str2[10] = "abcdef";就有sizeof(str2) == 10,因为str2的类型是char[10]

5)对于void func(char sa[100],int ia[20],char *p);

sizeof(sa) == sizeof(ia) == sizeof(p) == ,

因为前面有说过编译器把数组类型的书写的形参,解释为普通的指针类型

sa的类型是char*, ia的类型是int*,p的类型是char*

对于C/C++中的 字符指针和字符数组,总是在碰到的时候无法确定而不得不现场测试,来确定末尾是否包含'\0',函数到底如何使用等等。真是劳民伤财,现在总结一下:

字符指针的赋值

(1)指向一个字符串常量

char *src = "abcded"; //这种方式由系统自动给该字符指针指定了一个内存中的位置,并且该位置的字符数组为{'a', 'b', 'c', 'd', 'e', 'd', '\0'};

如果此时再次对 src赋值,src = "mmmt", 则src指向另外一个由系统指定的内存块(由"mmmt"本身返回)。这种方式赋值的src为一个指向字符串常量指针,不能对src指向的位置的内容做改变的操作,即不能执行 *src = 'a', *(src+1) = 't' 等操作;但是可以改变src指向的位置,即像之前的 src = "mmmt";

(2)指向一个字符数组

char tmp[4] = {'a', 'c', 'e', 'f'};

char* src = tmp;

(3)使用  new,然后可以像字符数组一样赋值,即指向一个字符数组

char* src = new char[10]; //这种方式由程序在堆内存上开辟了一个数组,并将地址赋值给src

字符串常量和字符数组比较

(1)字符串常量由系统自动分配一个内存区域,且该区域中的内容不能改变(即无法通过指向该字符串的指针进行修改);

(2)字符数组或者为系统自动分配的全局数据区或栈上的内存,或者通过new操作来分配的堆上的内存,字符数组中的内容可变(即可以通过指向该字符数组的指针进行修改)

(3)字符数组中不默认含有'\0',除非明确赋值,而字符串常量在末尾自动含有 '\0'.

strcpy的使用

(1)用strcpy时候, 如果源字符串是一个字符指针,则没有问题,因为字符指针自动带'\0',在'\0'位置复制结束;

  而如果源是一个字符数组(即将字符数组转换为字符指针来使用),则将会从字符数组的首地址开始复制,如果字符数组中明确指定了'\0'元素,则会在'\0'处停止,而若没有'\0'元素,则程序可能会不停的复制,直到在程序的内存中碰到'\0',这样可能会得到不希望的结果。

字符串赋值给字符指针(char *a="hello")的正确理解方式的更多相关文章

  1. 【转】C语言中,为什么字符串可以赋值给字符指针变量

    本文是通过几篇转帖的文章整理而成的,内容稍有修改: 一. C语言中,为什么字符串可以赋值给字符指针变量 char *p,a='5';p=&a;                     //显然 ...

  2. C语言中,为什么字符串可以赋值给字符指针变量

    转载于:http://www.cnblogs.com/KingOfFreedom/archive/2012/12/07/2807223.html 本文是通过几篇转帖的文章整理而成的,内容稍有修改: 一 ...

  3. C# 接收C++ dll 可变长字节或者 字符指针 char*

    网络上查找到的几乎都是 需要提前固定知道 接收字符(字节)数据的大小的方式,现在的数据大小方式 不需要提前知道如下 思路: 1 .C++,返回变长 指针或者字节 的地址给C# 接收,同时返回 该地址的 ...

  4. C语言基础复习:字符,字符数组,字符串,字符指针

    1. 概述2. 字符2.1 字符定义和大小2.2 字符的输入和输出2.3 字符的计算3. 字符数组3.1 字符数组的定义和大小3.2 字符数组的输入和输出3.3 字符数组的计算4. 字符串4.1 字符 ...

  5. c++字符指针

    对于C/C++中的 字符指针和字符数组,总是在碰到的时候无法确定而不得不现场测试,来确定末尾是否包含'\0',函数到底如何使用等等.真是劳民伤财,现在总结一下: 字符指针的赋值 (1)指向一个字符串常 ...

  6. VS2019 字符串对指针char*赋值编译器报错原因及解决方法

    2019-05-26   21:55:08 前几天在敲代码时,将字符串“Hellow world!”赋值给指针char*类型指针时编译器报错的问题 网上搜索后发现 char*是历史遗留问题,如果程序修 ...

  7. 字符数组,字符指针,字符串常量,以及sizeof的一些总结

    1.以字符串形式出现的,编译器都会为该字符串自动添加一个\0作为结尾 如在代码中写"abc",编译器帮你存储的是"abc\0". 2.数组的类型是由该数组所存放 ...

  8. C++ code:char pointers and char arrays(字符指针与字符数组)

    C-串的正确赋值.复制.修改.比较.连接等方式. #include<iostream> #pragma warning(disable: 4996)//这一句是为了解决“strrev”出现 ...

  9. Delphi 的内存操作函数(1): 给字符指针分配内存( 给字符指针(PChar、PWideChar、PAnsiChar)分配内存最佳的选择是StrAlloc。分配内存的时候会对字符串进行初始化)

    马上能想到的函数有: GetMem AllocMem ReallocMem FreeMem GetMemory ReallocMemory FreeMemory New Dispose NewStr ...

随机推荐

  1. NumPy的基本用法

    NumPy简介:NumPy是高性能科学计算和数据分析的基础包.是pandas等其他各种工具的基础NumPy主要功能:ndarray,一个多维数组结构,高效且节省空间无需循环对数组数据进行快速运算的数学 ...

  2. 解决Maven无法下载fastdfs-client-java依赖,Dependency 'org.csource:fastdfs-client-java:1.27-SNAPSHOT' not found.

    因为fastdfs-client-java-1.27-SNAPSHOT.jar这个依赖包在maven中央仓库是没有的, 需要自己编译源码成jar本地安装到maven 的本地仓库,安装完以后就能正常引用 ...

  3. 向MIP开源项目提交Issues

    Issues 是 GitHub 管理需求,讨论技术方案的方式,附:官方解释.MIP 是在 GitHub 上的开源项目,也使用 Issues 来做任务管理. 一.Issues 在 MIP 项目中的应用 ...

  4. ASP.NET Core 身份验证(一)

    前言 这篇文章我想带领大家了解一下 ASP.NET Core 中如何进行的身份验证,在开始之前强烈建议还没看过我写的 Identity 系列文章的同学先看一下. Identity 入门系列文章: Id ...

  5. Ons 让人欲哭无泪问题,官方介绍不详

      订阅不一致导致消费时成功时失败问题: Console下使用.Net SDK 访问队列成功. Windows 服务下使用.Net SDK 访问队列成功.   有时成功,有时则没有任何反应-   解决 ...

  6. Java数据结构和算法 - 栈和队列

    Q: 栈.队列与数组的区别? A: 本篇主要涉及三种数据存储类型:栈.队列和优先级队列,它与数组主要有如下三个区别: A: (一)程序员工具 数组和其他的结构(栈.队列.链表.树等等)都适用于数据库应 ...

  7. PHP内核之旅-6.垃圾回收机制

    回收PHP 内核之旅系列 PHP内核之旅-1.生命周期 PHP内核之旅-2.SAPI中的Cli PHP内核之旅-3.变量 PHP内核之旅-4.字符串 PHP内核之旅-5.强大的数组 PHP内核之旅-6 ...

  8. ASP.NET Core - 开篇

    由来 ASP.NET Core 是一个跨平台的高性能开源框架,ASP.NET Core第一次出现在我们眼前是以 ASP.NET vNext 命名的,然后又重新命名为ASP.NET 5,为了表明它并不是 ...

  9. API接口通讯参数规范(2)

    针对[API接口通讯参数规范]这篇文章留下的几个问题进行探讨. 问题1 试想一下,如果一个http请求返回一个500给我们,那我们是不是都不用看详情都知道该次请求发生了什么?这正是一个标准的结果码意义 ...

  10. BugkuCTF~代码审计~WriteUp

    第一题:extract变量覆盖 知识简介 extract()函数语法: extract(array,extract_rules,prefix) 参数 描述 array必需. 规定要使用的数组. ext ...