C/C++中的指针使用详解

C/C++ 指针

1 什么是指针?

指针就是内存地址,所有的变量除了值之外都有相应的地址,使用&运算符可以获得变量的地址.

int main(){
	int a = 10;
	cout << "a=" << a << endl << "a的地址为" << &a << endl;
}

输出如下:

a=10
a的地址为00E8FD08

我们可以使用type* p_name的方式去定义一个指针,并为他赋予一个地址作为值,如下:

int main()
{
	int a = 10;
	cout << "a=" << a << endl << "a的地址为" << &a << endl;
	int* p = &a;
	int* ap;
	ap = &a;
	cout << "p=" << p << endl << "*p=" << *p << endl << "&p=" << &p << endl;
	cout << "ap=" << ap << endl << "*ap=" << *ap << endl << "&ap=" << &ap << endl;
	return 0;
}

输出:

a=10
a的地址为00B5FE14
p=00B5FE14
*p=10
&p=00B5FE08
ap=00B5FE14
*ap=10
&ap=00B5FDFC

这意味着如果两个指针所指向的变量是相同的,那么指针所储存的地址也是相同的.这意味着地址和变量一一对应,但是可以看见app本身的地址并不相同.

值得注意的是:所有的实际数据类型,不管是整型,浮点型,字符型,还是其他的数据类型对应的指针的值的数据类型都是一样的,都是一个代表内存地址的16进制数,不同数据类型的指针仅有所指向的变量或者常量的数据类型不同.

int main()
{
	int* ap;
	double* bp;
	float* cp;
	char* dp;
	cout << "ap:" << sizeof(ap) << "	" << "int为" << sizeof(int) << endl;
	cout << "bp:" << sizeof(bp) << "	" << "double为" << sizeof(double) << endl;
	cout << "cp:" << sizeof(cp) << "	" << "float为" << sizeof(float) << endl;
	cout << "dp:" << sizeof(dp) << "	" << "cahr为" << sizeof(char) << endl;
	return 0;
}

输出结果如下:

ap:4    int为4
bp:4    double为8
cp:4    float为4
dp:4    cahr为1 

可以看到无论什么类型的指针,所占的内存大小都是4字节.

注:实际上指针所占用的大小与当前系统有关,如果是32位(x86)就是4字节,64位(x64)就是8字节,笔者所使用编译器为Visual Studio,设置为32位运行环境

2 如何使用指针?

指针最常用的几个操作如下

  1. 定义一个指针
    • type* p_name
    • int* p;
  2. 把变量地址赋值给指针
    • p_name=&var_name
    • p=&a;
  3. 访问指针变量中可用地址的值(指针的解引用)
    • *p_name
    • *p

3 空指针与野指针

3.1 空指针

在变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个NULL值是一个良好的编程习惯,即被赋予NULL作为值的指针被称为空指针

NULL为定义在标准库中的一个值为零的常量

int main()
{
	int* p = NULL;
	cout << p << endl;
	return 0;
}

输出结果如下

00000000

在大多数系统上,不允许访问地址为0的内存区域,因为该内存区域为操作系统所保留,然而,内存地址为零有特别重要的意义,他表示该指针不指向可以访问的内存区域,但是按照惯例,如果指针包含空值,则假定他不指向认可东西

可以使用如下语句来检测是否为空指针

if(p)//如果p不为空指针,执行
if(!p)//如果p是空指针,执行

3.2 野指针

野指针指的是指向了非法的内存空间的指针,野指针无法进行操作,所以在实际编程过程中尽量避免出现野指针.

int* p =(int*)0x1332;//0x1332是一个十六进制数字,转换为int*类型

4 使用const修饰指针

4.1 const修饰指针

const修饰指针被称为常量指针

int a = 100;
const int* p = &a;

我们可以对p进行任何操作,但是不可以对*p进行任何操作.

int main()
{
	int a = 100;
	int b = 10;

	const int* pb = &b;
	int* pa = &a;

	cout << "a=" << a << " " << "b=" << b << endl;
	cout << "*p=" << *pb << " " << "*pa=" << *pa << endl;

	//*pb = 5;
	*pa = 50;

	cout << "a=" << a << " " << "b=" << b << endl;
	cout << "*p=" << *pb << " " << "*pa=" << *pa << endl;
	return 0;
}

输出如下:

a=100 b=10
*p=10 *pa=100
a=50 b=10
*p=10 *pa=50

如果将*pb=5取消注释,则会报如下错误:

表达式必须是可修改的左值

如果想要更改p对应的值,不应该对*p做操作,可以更改p的指向或者a的值.

4.2 const修饰常量

使用const修饰常量被称为指针常量

int* const p = &a;

与常量指针相反,对于指针常量,我们可以修改*p,但是不可以修改p

int main()
{
	int a = 100;
	int b = 10;

	int* const pb = &b;
	int* pa = &a;

	cout << "a=" << a << " " << "b=" << b << endl;
	cout << "*p=" << *pb << " " << "*pa=" << *pa << endl;

	*pb = 5;
	*pa = 50;
	//pb = &a;

	cout << "a=" << a << " " << "b=" << b << endl;
	cout << "*p=" << *pb << " " << "*pa=" << *pa << endl;
	return 0;
}

如上程序发现*pb=5;一句可以正确执行,但是pb=&a;一句则无法执行.

取消注释,同样会报如下错误:

表达式必须是可修改的左值

4.3 const修饰指针和常量

const int* const p = &a;

使用如上语句同时具备指针常量和常量指针的特点,我们既不可以对p进行操作,也不可以对*p进行操作.

若要修改p所对应的值,应该修改其指向的变量a;

5 指针的运算符

5.1 算术运算符

指针的值是一个内存地址,这意味着指针同样可以进行算术运算,

指针允许有以下四种运算:++,--,+,-.

如下:

int main()
{
	int a = 100;
	double b = 10.0;
	char c = 'a';

	int* p_int = &a;
	double* p_double = &b;
	char* p_char = &c;
    
	cout << "p_int=" << (int)p_int << endl;
	cout << "p_double=" << (int)p_double << endl;
	cout << "p_char=" << (int)p_char << endl;
    
	p_int++;
	p_double++;
	p_char++;
    
	cout << "p_int=" << (int)p_int << endl;
	cout << "p_double=" << (int)p_double << endl;
	cout << "p_char=" << (int)p_char << endl;
    
	return 0;
}

输出结果如下:

p_int=18086144
p_double=18086128
p_char=18086119
p_int=18086148
p_double=18086136
p_char=18086120

修改至十进制后可以清楚的看到,p_int增加了4,p_double增加了8,p_char增加了1;

可以得到以下结论:

  • 指针的每一次递增,都会指向下一个元素的地址
  • 指针的每一次递增,都会指向上一个元素的地址
  • 指针在递增和递减是移动的大小决定于指针的类型
    • double类型的指针为8字节
    • int类型的指针为4字节等.

5.2 关系运算符

指针可以使用关系运算符进行比较,常见的有==,!=,>,<等等

int main()
{
	int a = 100;
	int b = 10;
	int c = 50;
	int* p = &a;
	if (p==&a)
	{
		cout << "p==a" << endl;
	}
	else if (p==&b)
	{
		cout << "p==b" << endl;
	}
	else
	{
		cout << "p==c" << endl;
	}
	return 0;
}

输出如下:

p==a

5.3 实际应用

指针使用算术运算符和关系运算符可以对数组进行访问.

因为数组名类似于一个指针常量,我们无法对一个数组进行自增自减操作,所以我们经常使用指针代替数组.

使用指针遍历数组如下:

int main()
{
	int a[5] = {9,8,7,6,5};
	int* pa = a;
	for (int i = 0; i < 5; i++)
	{
		cout << pa << "	" << *pa << endl;
		pa++;
	}
	return 0;
}

结果如下:

0079FBF8        9
0079FBFC        8
0079FC00        7
0079FC04        6
0079FC08        5

同样可以使用指针自减来访问数组:

int main()
{
	int a[5] = {9,8,7,6,5};
	int* pa = &a[4];
	for (int i = 0; i < 5; i++)
	{
		cout << pa << "	" << *pa << endl;
		pa--;
	}
	return 0;
}

结果如下:

00EFF9F4        5
00EFF9F0        6
00EFF9EC        7
00EFF9E8        8
00EFF9E4        9

同样可以使用while语句,以指针使用关系运算符作为条件

int main()
{
	int a[5] = { 9,8,7,6,5 };
	int* pa = a;
	while (pa <= &a[4])
	{
		cout << pa << "\t" << *pa << endl;
        //'\t'是转义字符,读者可以自行查阅转义字符相关知识
		pa++;
	}
	return 0;
}

结果如下:

00A5FA38        9
00A5FA3C        8
00A5FA40        7
00A5FA44        6
00A5FA48        5

6 指针指向指针

指针同样可以指向指针,即指针链,比如:指针a指向指针b,指针b指向值c;

如下:

int main()
{
	int c = 10;
	int* b = &c;
	int** a = &b;
	cout << "c=" << c << "\t&c=" << &c << endl;
	cout << "*b=" << *b << "\tb=" << b << "\t&b=" << &b << endl;
	cout << "**a=" << **a << "\t*a=" << *a << "\ta=" << a << endl;
	return 0;
}

输出如下:

c=10    &c=00B3F7C0
*b=10   b=00B3F7C0      &b=00B3F7B4
**a=10  *a=00B3F7C0     a=00B3F7B4

可以看到b指向了c的地址,而a指向了b的地址,所以对a解引用一次得到的是b的值,即c的地址,再解引用一次就是c的值.

7 指针与函数的使用

指针既可以作为返回值类型也可以作为函数的参数列表

如下:

int* changeP(int* M_p) {
	int M_a = 100;
	M_p = &M_a;
	return M_p;
}

int main()
{
	int a = 10;
	int* p = &a;
	cout << "*p=" << *p << endl;
	cout << "a=" << a << endl;
	p = changeP(p);
	a = *changeP(&a);
	cout << "*p=" << *p << endl;
	cout << "a=" << a << endl;
	return 0;
}

输出如下:

*p=10
a=10
*p=100
a=100

注意:本例子中changeP()返回的是一个int*类型的值,故在赋值于a的时候,应该解引用;

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: memset()函数是C/C++语言的一个库函数,可以用于将一段内存空间的内容设置为指定的。 memset()函数的原型为: ```c void *memset(void *s, int c, size_t n); ``` 其,s是指向要设置的内存的指针;c是要设置的,通常是一个无符号字符或者零;n是要设置的内存空间的大小。 memset()函数的作用是将一段内存空间的每个字节都设置为相同的。可以用于初始化内存,或者将内存的内容清零。 使用memset()函数需要注意以下几点: 1. memset()函数只能用于字符数据类型或者无符号整型数据类型,即只能设置1字节大小的。 2. 使用memset()函数时,需要知道要设置的内存空间的大小,以防止超出边界进行内存越界操作。 3. memset()函数是按字节进行设置的,所以对于非字符类型数据(如整数或浮点数),可能造成数据不符合预期。 示例代码: ```c #include <string.h> int main() { int arr[5]; memset(arr, 0, 5 * sizeof(int)); // 将arr内存空间设置为0 char str[20]; memset(str, 'A', 19); // 将str内存空间设置为'A' str[19] = '\0'; // 末尾添加'\0',形成一个字符串 return 0; } ``` 总之,memset()函数是一个能快速设置内存空间内容的函数,可以方便地进行内存初始化和内存清零操作。 ### 回答2: memset()函数是C语言的一个库函数,其原型如下: void *memset(void *ptr, int value, size_t num); memset()函数的作用是将指定内存空间的设置为指定的。其,ptr表示要设置的内存空间的起始地址,value表示要设置的,num表示要设置的字节数。 memset()函数的返回为void指针类型,即可以接受任何类型的指针使用memset()函数可以在一次调用批量设置内存空间的,提高效率和代码的简洁度。 例如,下面的代码片段就是使用memset()函数将数组arr的所有元素设置为0: int arr[10]; memset(arr, 0, sizeof(arr)); 由于memset()函数设置的是字节数据,所以在设置非字符类型数据时需要小心。以一个int型数组arr为例,使用memset()函数将其所有元素设置为-1可能会出现错误。因为在某些机器上,-1的二进制表示并不是所有字节全为1,这会导致memset()函数设置的结果并非预期。 对于字符数组或字符串,可以使用memset()函数设置为0,即'\0',也可以使用strcpy()函数进行单个字符赋,这样更为安全可靠。 总之,memset()函数是一个实用的函数,可以批量设置内存空间的,提高代码的执行效率和简洁度。在使用时,需要注意数据类型和数据源的合法性,以避免出现错误。 ### 回答3: memset()函数是C/C++语言的一个库函数,主要用来对一段指定内存空间进行初始化。 其函数原型为: void* memset(void* ptr, int value, size_t num); 第一个参数ptr是一个指向某一块内存区域的指针,用来指定待初始化的内存空间。 第二个参数value是一个整数,用来指定待初始化的,其最常用的是0。 第三个参数num是一个整数,用来指定待初始化的内存空间的字节数。 memset()函数的作用是将ptr指向的内存空间的每个字节都设置为value指定的。一般来说,value为0时,可以用来将内存空间清零。 memset()函数通常用于对数组、字符串或结构体等数据类型的初始化。例如,当我们声明一个数组或字符串后,需要将其所有元素或字符都初始化为0,可以使用memset()函数。 以下是一个使用memset()函数进行数组初始化的示例: int num[5]; memset(num, 0, sizeof(num)); 以上代码将会把num数组的所有元素都初始化为0。 需要注意的是,memset()函数只能用来设置每个字节的,并不能对较大的数据块进行初始化。此外,在使用memset()函数时,需要确保待初始化的内存空间不受限制并且是可访问的,否则可能会引发错误。 总结起来,memset()函数是C/C++常用的一个函数,主要用来对一段指定的内存空间进行初始化,提高程序的可读性和可维护性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值