🌈个人主页:godspeed_lucip
🔥 系列专栏:《C++程序设计》阅读笔记
本文对应的PDF源文件请关注微信公众号程序员刘同学
,回复C++程序设计
获取下载链接。
- 1 指针
- 1.1 字符指针
- 1.1.1 字符串的表示
- 1.1.2 字符串的属性
- 1.1.3 字符指针
- 1.1.4 字符串比较
- 1.1.5 字符串赋值
- 1.2 指针数组
- 1.2.1 概述
- 1.2.2 指针数组和二维数组的不同
- 1.2.3 指向指针的指针
- 1.2.4 NULL指针值
- 1.3 命令行参数
- 1.3.1 概念
- 1.4 函数指针
- 1.4.1 概述
- 1.4.2 typedef简化
- 1.4.3 例子
- 2 总结
1 指针
1.1 字符指针
1.1.1 字符串的表示
字符串的类型是指向字符的指针(字符指针char *)。例如char *buffer[]="hello";
字符串在内存中以\0
结尾。
当字符串用于字符数组初始化时,其在完成将内容填写到所创建的字符数组中之后,随即消失,不再另辟存储空间;而当字符串用于表达式,或输出,或赋值,或作参数传递,则其在运行中有它自己的存储空间,可以寻址访问
1.1.2 字符串的属性
字符串通常存放在内存的 const 区。而字符数组则不同,如果字符数组是全局变量,存放在全局或静态区;如果字符数组是局部数据,就存放在内存的栈区
每个字符串都会被单独存储,即使两个字符串的内容完全相同,但是也会被存储在两个不同的空间。而字符串的比较实际上是存储地址的比较。因此,下面的代码:
结果为not equal
1.1.3 字符指针
字符串、字符数组名、字符指针均属于同一种数据类型
解释:
当编译器遇到一字符串时,就把它放到字符串池(data 区的 const 区)中,以
'\0'
作结束符,记下其起始地址,在所构成的代码中使用该地址。所以,字符串会自动被转换为地址而字符数组名是一个指向数组第一个元素(字符)的指针。
字符指针是指向对应字符的的指针
所以这三个数据类型本质上都是“地址”。
下面的例子中:
结果为:
请注意:虽然pc
指向的是字符串hello
的第一个字符,但是当使用 cout << pc;
时,cout
会根据 pc
指向的地址开始输出字符,直到遇到空字符 '\0'
为止。因此,输出的结果是整个字符串 "hello"
。之后输出ABC
同理。
输出字符指针就是输出字符串。输出字符指针的间接引用,就是输出单个字符(重在理解,不要死记硬背)
1.1.4 字符串比较
要想真正的比较字符串的内容,可以使用strcmp
(包含string头文件#include<string>
)
int strcmp(const char* strl,const char * str2);
1.1.5 字符串赋值
C++中可以用字符串去初始化字符数组,但是不能对字符数组赋予一个字符串。原因是数组名是常量指针,不是左值。但是可以改变数组中的值。例如:
char buf[10] = "ABC"; // ok
buf[0] = ’C‘; //ok
buf = "IJN"; // error
TIP:字符数组的修改
- 非常量字符数组:
char str[] = "Hello"; str[0] = 'C'; // 合法,修改字符数组的内容
- 常量字符数组:
const char str[] = "Hello"; // str[0] = 'C'; // 错误,无法修改常量字符数组的内容
可以使用strcpy
对字符数组赋值:
#include<string>
char bufferll10];
char buffer2[10];
strcpy(buffer1,"hello");
strcpy(buffer2,buffer1);
函数
strcpy()
仅能对以'\0'
作结束符的字符数组进行操作
1.2 指针数组
1.2.1 概述
下面的代码就定义了一个字符指针数组。虽然里面的”内容“是字符串。但是字符串是存储在常量区的(可能连续也可能不连续),前面已经说过,编译器会以地址的形式去使用字符串,所以proname数组中存储的就是字符指针(连续存储)。
char * proname = {"Fortran","C","C++"}
1.2.2 指针数组和二维数组的不同
在前面的例子中,proname
本身包含3个指针(指针的大小都是一定的),而proname[0]
指向的字符串包含8个字节(包含\0
字符),同理proname[1]、proname[2]
分别包含2个字节、4个字节。
而二维数组的每一行的大小都是相等的,所以假如上述内容使用二维数组存储,其所占的内存空间就是8*3=24字节
。
1.2.3 指向指针的指针
指针数组也是数组,而数组名是指针常量(可以近似这样理解),指向数组第一个元素的起始地址,而指针数组中存储的是指针,因此:指针数组名是指向指针的指针(二级指针)
为什么说可以近似这样理解:
- 数组名本质上不是指针
#include<iostream> using namespace std; int main(){ size_t size = sizeof(void*); // 获取指针大小(字节) cout << "Size of a pointer: " << size << " bytes\n"; char * proname[] = {"Fortran","C","C++"}; //获取数组名的大小 cout<<"数组的大小;"<<sizeof(proname)<<"bytes"<<endl; }
结果:
假如数组名就是一个指针,那么数组名大小应该也是8比特,但是显然不是。
- 数组名会"退化"为指针
例如,看下面的例子:
#include<iostream> using namespace std; void test01(char *arr[]){ cout<<sizeof(arr)<<endl; } int main(){ size_t size = sizeof(void*); // 获取指针大小(字节) cout << "Size of a pointer: " << size << " bytes\n"; char * proname[] = {"Fortran","C","C++"}; //获取数组名的大小 cout<<"数组的大小;"<<sizeof(proname)<<"bytes"<<endl; test01(proname); }
运行结果:
可以看到,数组在作为参数被传递时,其大小是一个指针的大小。因为为了不让数组的内容全部复制一份到内存中,编译器实际上只会传递数组的地址,此时数组名就只想第一个元素的地址,也就是”退化“成了指针。
奇怪的是,C++程序设计教程(第三版)明确说,数组名就是指针。个人觉得值得商榷。
by the way,如果想输出指针的地址,可以这样:(int)指针
。
1.2.4 NULL指针值
NULL
与 void *
是不同的概念。NULL
是一个值,一个指针值,任何类型的指针都可赋予该值;而void *
是一种类,,它定义无类型指针。
1.3 命令行参数
1.3.1 概念
C++中main
函数的参数声明,用于接收命令行参数。
int main(int argc, char* argv[]) {
// ...
}
int argc
:这是参数计数(argument count)的缩写。它表示命令行参数的数量,包括程序名称本身。即,argc
给出了传递给程序的命令行参数的个数,至少为1(程序名称本身)。char* argv[]
:这是参数向量(argument vector)的缩写。它是一个指向字符指针数组的指针,每个字符指针指向一个命令行参数字符串。数组的第一个元素(argv[0]
)通常是程序的名称,后续元素是从命令行传递给程序的实际参数。
例如,如果你在命令行中运行程序 ./myprogram arg1 arg2
,那么:
argc
将为 3(程序名称myprogram
、arg1
和arg2
三个参数)。argv
是一个指针数组,其中argv[0]
指向程序名称字符串,argv[1]
指向arg1
,argv[2]
指向arg2
。
通过使用 argc
和 argv
,你可以在程序中处理和利用从命令行传递的参数。
1.4 函数指针
1.4.1 概述
函数指针的的定义:(注意一个括号,去掉这个括号就表示一个返回值为指针的普通函数)
函数存放在代码区。
不同类型之间的函数指针不可以相互赋值。
函数指针和其他数据类型的指针之间不可以相互赋值(即使显示转换也不行)。因为从底层来说,数据指针指向data区、stack区、heap区,而函数指针指向code区,是代码。
fp1
的类型可以视为int(char,char)
,fn1
的类型可以视为int(char,char)
。因此fp1=fn1
可以通过。此时,fp1
就是一个指针,它指向了函数fn1
。不能这样写:
fp1=fn1('A','B')
,这相当于将fn1
的返回值赋值给了fp1
。(编译器会报错的)或者也可以这样写:
int (*fn1_pointer)(char,char) = fn1
此时,
fn1_pointer
就成为了函数fn1
的指针
1.4.2 typedef简化
1.4.3 例子
下面的例子中,将cmath
库中的sin
、cos
函数作为函数指针传入d_sum
,计算其和
#include<iostream>
#include<cmath>
using namespace std;
double d_sum(double(*func)(double), double d1, double d2){
double sum;
for(;d1<d2;d1+=0.1){
sum+=func(d1);
}
return sum;
}
int main(){
cout<<d_sum(sin,0.1,1.0)<<endl;
cout<<d_sum(cos,0.5,3.0)<<endl;
return 0;
}
结果:
2 总结
C++,犹如编程的交响乐, 在代码的海洋中奏响和谐的旋律。
它是创造者的笔,雕刻着无尽可能,
是思想的翅膀,让梦想飞翔的天空。
无拘无束,灵活多变。
C++,是程序员心中的宝藏,永不凋零的花朵。
渴望挑战C++的学习路径和掌握进阶技术?不妨点击下方链接,一同探讨更多C++的奇迹吧。我们推出了引领趋势的💻C++专栏:《C++程序设计》阅读笔记,旨在深度探索C++的实际应用和创新。🌐🔍