地址与值的更改

main        说到地址,在C语言中一般都与指针挂钩,我们可以利用指定的指针变量来存储特定的内存块的首地址。指针可以可以指向的变量包括整型变量、浮点型变量、结构体变量、指针变量乃至字符串和函数,因为这些事物都是占据内存空间的实体。所以,指针的便利之处在于其能够储存某个变量的首地址,并通过首地址访问或修改在这个内存空间的值。下面举一个例子:

//代码清单1
#include<stdio.h>
#include<string.h>
int main()
{
	char str[25]="Be stronger,be helpful!";
	printf("%s\n", str);
	char *str_ptr = str;
	str_ptr[strlen(str) - 1] = '.';
	printf("%s", str);
	return 0;
}

执行结果是什么呢?如图:

        在第5行代码中,我们定义了一个字符串并往它里面添加了一些字符,然后我们在第7行定义了一个字符指针并且用 str[] 数组的首地址 str 为它赋初值,之后在第8行通过 str_ptr 将 str[] 字符数组的最后一个字符由 ‘ ! ’ 替换为 ‘ . ’ ,通过6、9行的输出语句分别输出被替换字符前后储存在 str[] 内的字符串。我们发现,通过这样的操作,我们的确修改了 str[] 内的字符串。

        与此类似地,我们可以将字符串修改的这一操作推广到各种变量,在其中,掌握指向指针变量的指针也是很重要的。我们举一个例子:

//代码清单2
#include<stdio.h>
#include<string.h>
int main()
{
	char **str_ptr = NULL;
	char *str[2] = { "Be stronger,be helpful!","Hold your dream." };
	str_ptr = str;
	printf("%s\n", str_ptr[0]);
	printf("%s", str_ptr[1]);
	return 0;
}

在第5行代码中,我定义了一个二重指针 str_ptr ,而在第6行代码中则定义了一个含有两个字符指针作为元素的一个一维数组,第7行中将 str 作为初值赋给了 str_ptr 。为什么能将一个一维数组的首地址赋给一个二重指针呢?原因只有一个,str 此时是一个“二重指针常量”,我找不到更好的词来说明 str 的性质了,但显然,我们将常量值赋给变量,这是很合理的一种想法。我们还可以这样来理解这个“二重指针常量”:str[]中的元素本身就是字符指针,故 str[0]、str[1] 就已是普通的字符指针了,可以理解为“一重指针常量”,这种字符指针指向普通的字符串;而 str[] 又是有着两个元素的数组,str 表示的正是它的首地址,故 str 相对于自身元素而言又是“一重指针常量”,由于其元素本身已是“一重指针常量”,我们可以认为相对于普通的指针,str 可以理解为“二重指针常量”。至于三重指针,也是一样的道理,不过更加复杂了。

        另外,指针的应用显然不止这么简单,如果我们在为函数传参数时,想要为想要改变传递的参数的值,我们需要这个参数的地址,举一个例子:

//代码清单3
#include<stdio.h>
typedef struct stu
{
	char id[3];
	char name[10];
}stu;
int modify(stu *stud)
{
	strcpy(stud->id , "7");
	strcpy(stud->name, "Log");
	return 0;
}
int main()
{
	stu student;
	stu *stude = &student;
	strcpy(student.id, "6");
	strcpy(student.name, "Rog");
	printf("%s :%s\n", student.id, student.name);
	modify(stude);
	printf("%s :%s", student.id, student.name);
	return 0;
}

结果为:

可以看到, 通过将 modify() 的形参设置为 stu* 类型,我们在第20行将同类型的变量 stude 传递给了 modify() ,然后 student 的值的确在 modify() 函数内被改变了。那就要问了,如果我们不将指针 stude 传给这个函数,我们能直接将一个 stu 类型而非 stu* 类型的变量作为 modify() 的形参从而改变 student 的值吗?我们不妨试试:

//代码清单4
#include<stdio.h>
typedef struct stu
{
	char id[3];
	char name[10];
}stu;
int modify(stu stude)
{
	stu *stud = &stude;
	strcpy(stud->id , "7");
	strcpy(stud->name, "Log");
	return 0;
}
int main()
{
	stu student;
	strcpy(student.id, "6");
	strcpy(student.name, "Rog");
	printf("%s :%s\n", student.id, student.name);
	modify(student);
	printf("%s :%s", student.id, student.name);
	return 0;
}

结果为:

student 的值并没有改变,因此这种方法是行不通的。原因?

C语言使用一种被称为“按值”的机制,给函数传递参数。这是因为,当使用变量作为函数参数的时候,C语言并不是把变量的地址传给函数,而是传递变量的值。

                                          ————《C语言实用之道》[美] Giulio Zambon 著    潘爱民 译

因此,这串代码中,我们只是将 student 的值拷贝给了 modify() 的形参 stude ,当我们在第20行调用 modify() 的时候,实际上我们只是给它传了 student 的值,然后很快就将这个值给了 stude ,接下来在 modify() 中起作用的实际上是 stude ,而不是 student 。这样看来,student 和 stude 实际上是两个不同的变量,我们可以试验一下,只需将代码清单4的代码改为:

//代码清单5
#include<stdio.h>
typedef struct stu
{
	char id[3];
	char name[10];
}stu;
int modify(stu stude)
{
	stu *stud = &stude;
	strcpy(stud->id, "7");
	strcpy(stud->name, "Log");
	printf("stude's address:%p\n", &stude);
	return 0;
}
int main()
{
	stu student;
	strcpy(student.id, "6");
	strcpy(student.name, "Rog");
	printf("student's address:%p\n", &student);
	printf("%s :%s\n", student.id, student.name);
	modify(student);
	printf("%s :%s", student.id, student.name);
	return 0;
}

结果为:

在我的PC机上可以看到 student 和 stude 的确是被存储在不同的内存单元的,而这正说明了 student 和 stude 是两个不同的变量。

        有的时候,我们也可以定义一个字符指针来达到行使字符数组的功能,例如:

//代码清单6
#include<stdio.h>
int main()
{
	char *str = "Be stronger,be helpful!";
	/*
	char *str = NULL;
	str = "Be stronger,be helpful!";
	*/
	//第7、8行的为str赋值的方式也是正确的
	
	/*
	char str[25];
	str = "Be stronger,be helpful!";
	*/
	//第13、14行的为str赋值的方式是错误的,
	//因为第13行定义的str[]数组被分配了内存,
	//str是这块内存的首地址,已经是一个常量了
	printf("%s", str);
	return 0;
}

结果为:

        再说一下我们在每个程序中都必写的 main() 函数,它的完整形式为:

main(int argc,char *argv[])

 写一个小程序:

//代码清单7
#include<stdio.h>
int main(int argc, char *argv[])
{
	printf("当前文件为:%s", argv[0]);
	int iInt = atoi(argv[1]);
	printf("接收的第一个参数为: %d\n", iInt);
	iInt = atoi(argv[2]);
	printf("接收的第二个参数为: %d\n", iInt);
	iInt = atoi(argv[3]);
	printf("接收的第三个参数为: %d\n", iInt);
	printf("总共接收了 %d 个参数", argc - 1);
	return 0;
}

当然,它不能在编译器上运行,它是在控制台上运行的,接下来我将教你如何操作这个函数,我的运行环境是Visual Studio,我们先运行一下这个程序:

这是正常的,我们要的正是当前文件的路径,而编译器也只能给我们这个,接着程序便运行不下去了。得到了这个exe文件(可执行文件)路径,我们继续。

具体步骤为:

1、按下 Win+R 键

2、输入cmd

3、进入到要执行的exe文件所在目录

这里对第3个步骤进行详细讲解:

        首先,我利用在编译器中执行一次的方法得到了相应源文件的可执行文件,并且在我的PC机中它的路径为:C:\Users\86187\source\repos\博客专用\Debug\博客专用.exe                              那么,我需要在弹出的控制台窗口中输入 cd C:\Users\86187\source\repos\博客专用\Debug ,同时按下Enter键,画面为:

接着依次输入exe文件的名称和随便三个数字: 博客专用 12 23 34  ,我们就得到了下图所示的结果:

        可以看到,在控制台中我们看到了我们在源文件中写的代码清单7中的代码的执行结果。那么接下来,我讲一下 argv 和 argc 的意义,argc 默认值是1,代表只有数组 argv[] 的元素个数只有一个,即 argv[0] ,而 argv[0] 的意义大家想想自己是如何获取到自己的可执行文件路径的呢?没错,argv[0] 代表的正是由这个源文件生成的可执行文件的路径。说回来,argc 的大小代表着数组 argv[] 的元素个数,我们利用控制台执行了这个文件,输入了3个参数,分别为12、23、34,是没有用到输入函数如scnaf()、gets()的,这一点大家明确。另外,atoi() 函数可以将数字字符串转换成数字,atoi即alphabet to integer,想多了解可以戳这里

本篇完。


欢迎指正我的上一篇博客:递归实现双向链表

我的下一篇博客:一元多项式的相加和相减操作(链表)

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
指针传递和地址传递是两种不同的概念。在C语言中,函数参数可以通过传递或地址传递来传递给函数。 指针传递是指将指针变量作为参数传递给函数,函数中对指针所指向的内存进行操作。这种方式下,函数内部对指针的修改不会影响到原始的指针变量。但是,函数中对指针所指向的内存的修改会影响到原始内存。 地址传递是指将变量地址作为参数传递给函数。这种方式下,函数内部对地址所对应的变量进行操作,会直接影响到原始变量。通过地址传递,可以实现在函数内部对原始变量进行修改。 下面是一个示例代码,展示了指针传递和地址传递的区别: ```c #include <stdio.h> // 指针传递 void pointerValuePass(int* ptr) { *ptr = 10; // 修改指针所指向的内存 ptr = NULL; // 修改指针本身不会影响原始指针 } // 地址传递 void addressPass(int* ptr) { *ptr = 20; // 修改地址对应的变量 ptr = NULL; // 修改指针本身不会影响原始指针 } int main() { int num = 0; int* ptr = &num; printf("Before pointerValuePass: num = %d\n", num); pointerValuePass(ptr); printf("After pointerValuePass: num = %d\n", num); printf("Before addressPass: num = %d\n", num); addressPass(&num); printf("After addressPass: num = %d\n", num); return 0; } ``` 输出结果为: ``` Before pointerValuePass: num = 0 After pointerValuePass: num = 10 Before addressPass: num = 10 After addressPass: num = 20 ``` 可以看到,指针传递修改了指针所指向的内存,但没有修改原始指针;而地址传递直接修改了原始变量
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奔走的月光

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值