C语言错题整理1-2021-9.22

1-C语言三种参数传递方式


值传递、地址传递、引用传递
1)在函数定义格式上有不同:
值传递在定义:Exchg1(int x, int y);
地址传递定义:Exchg2(int *px, int *py);
引用传递定义:Exchg3(int &x, int &y);
2)调用时有相同的格式:
值传递:Exchg1(a, b);
地址传递:Exchg2(&a, &b);
引用传递:Exchg3(a, b);
3)简述不同
值传递:不能传出。当函数内部需要修改参数,并且不希望这个改变影响调用者时,采用值传递。
指针传递:形参为指向实参地址的指针,当对形参的指向操作时,就相当于对实参本身进行的操作
引用传递:形参相当于是实参的“别名”,对形参的操作其实就是对实参的操作,在引用传递过程中,被调函数的形式参数虽然也作为
局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量。

2-实参与形参


实参:实参可以是常量、变量、表达式、函数等;形参只能是常量、变量
形参:形参变量只有在被调用时才分配内存单元,在调用结束时, 即刻释放所分配的内存单元

注意:
1-形参和实参实际上占用的是两份不同的存储单元。
2-实参和形参在数量上,类型上,顺序上应严格一致, 否则会发生“类型不匹配”的错误。
3-函数调用中发生的数据传送是单向的。 即只能把实参的值传送给形参,而不能把形参的值反向地传送给实参。 因此在函数调用过程中,形参的值发生改变,而实参中的值不会变化。

3-以下程序段运行结果是

#include<stdio.h>
void main()
{
    short *p,*q;
    short arr[15]={0};
    p=q=arr;
    p++;
    printf("%d,",p-q);
    printf("%d,",(char*)p-(char*)q);
    printf("%d",sizeof(arr)/sizeof(*arr));
}

结果:1,2,3
指针自增、自减每次移动的偏移量是指针所指向对象的字节大小,所以p++与q的偏移量是2个字节。
指针相减的值是指针地址的偏移除以指针每次移位的大小;
1)p-q=1;偏移量为2个字节,每次移动2个字节,所以为1
2)(char *)p-(char )q,指针的偏移没变,但是每次指针移位是按照(char)类型移动,即每次移动1个字节,所以是2
3)数字每次元素2个字节,所以sizeof(arr)为30,sizeof(*arr)为2

4-const char *p、char * const p 和 const char * const p 的区别


1.const char* ptr :这是一个指向常量字符的指针。不能更改 ptr 指向的值,但可以更改指针本身。const char *是一个指向const char的(非常量)指针。

// C program to illustrate
// char const *p
#include<stdio.h>
#include<stdlib.h>
int main()
{
	char a ='A', b ='B';
	const char *ptr = &a;
	//*ptr = b; illegal statement (assignment of read-only location *ptr)
	// ptr can be changed
	printf( "value pointed to by ptr: %c\n", *ptr);
	ptr = &b;
	printf( "value pointed to by ptr: %c\n", *ptr);
}

输出:
ptr 指向的值:A
ptr 指向的值:B
注意:const char* p 和 char const* p之间没有区别,因为它们都是指向 const char 的指针,并且 ‘*’(asterik) 的位置也相同。

2.char *const ptr :这是一个指向非常量字符的常量指针。不能更改指针 p,但可以更改 ptr 指向的值。

// C program to illustrate
// char* const p
#include<stdio.h>
#include<stdlib.h>
int main()
{
	char a ='A', b ='B';
	char *const ptr = &a;
	printf( "Value pointed to by ptr: %c\n", *ptr);
	printf( "Address ptr is pointing to: %d\n\n", ptr);
	//ptr = &b; illegal statement (assignment of read-only variable ptr)
	// changing the value at the address ptr is pointing to
	*ptr = b;
	printf( "Value pointed to by ptr: %c\n", *ptr);
	printf( "Address ptr is pointing to: %d\n", ptr);
}

输出:
ptr 指向的值:A 地址 ptr 指向:-1443150762
ptr 指向的值:B 地址 ptr 指向:-1443150762
注意:指针总是指向同一个地址,只是该位置的值发生了变化。

3.const char * const ptr :这是一个指向常量字符的常量指针。既不能更改 ptr 指向的值,也不能更改指针 ptr。

// C program to illustrate
// char const *p
#include<stdio.h>
#include<stdlib.h>
int main()
{
	char a ='A', b ='B';
	const char *const ptr = &a;
	printf( "Value pointed to by ptr: %c\n", *ptr);
	printf( "Address ptr is pointing to: %d\n\n", ptr);
	// ptr = &b; illegal statement (assignment of read-only variable ptr)
	// *ptr = b; illegal statement (assignment of read-only location *ptr)
}

输出:
ptr 指向的值:A
地址 ptr 指向:-255095482
注意: char const * const ptr与const char *const ptr 相同。

5-内存布局、内存泄漏、内存管理


C 程序的典型内存表示由以下部分组成。
1.文本段
2.初始化数据段
3.未初始化数据段
4.堆栈
5.堆

在这里插入图片描述
内存泄漏:
当程序员在堆中创建内存并忘记删除它时,就会发生内存泄漏。
内存泄漏的后果是它通过减少可用内存量来降低计算机的性能。最终,在最坏的情况下,可能会分配过多的可用内存,并且系统或设备的全部或部分停止正常工作、应用程序失败或系统速度大幅降低。
内存泄漏对于守护进程和服务器等程序来说是特别严重的问题,这些程序根据定义永远不会终止。

6-运算符优先级


! > 算术运算符 > 关系运算符 > && > ||(逻辑运算符) >赋值运算符

7-while满足条件


while(!b_val)等价于while(b_val !=0),
while(!b_val)等价于while(b_val !==0)。

8-const用法


1-定义变量:修饰全局变量与局部变量

const int n=5;
int const n=5;

用 const 定义的变量的值是不允许改变的,即不允许给它重新赋值,即使是赋相同的值也不可以。所以说它定义的是只读变量。这也就意味着必须在定义的时候就给它赋初值。用 const 修饰全局变量和局部变量,生存周期都是程序运行的整个过程。全局变量的生存周期为程序运行的整个过程这个是理所当然的。而使用 const 修饰过的局部变量就有了静态特性,它的生存周期也是程序运行的整个过程。我们知道全局变量是静态的,静态的生存周期就是程序运行的整个过程。但是用const修饰过的局部变量只是有了静态特性,并没有说它变成了静态变量。
2-修饰指针

const int *p        //p本身不是const的,常量指针,而p指向的变量是const的
int const *p        //p本身不是const的,常量指针,而p指向的变量是const的
int * const p;      //p 本身是 const 的,常指针变量,而 p 指向的变量不是 const 的,该指针只能赋值一次, 且只能在定义时候赋值 ,但是指针指向的变量可以改变
const int * const p;//p 本身是 const 的,指向常量的长指针,而 p 指向的变量也是 const 的

1、常量指针说的是不能通过这个指针改变变量的值,但是还是可以通过其他的引用来改变变量的值的。
2、常量指针指向的值不能改变,但是这并不是意味着指针本身不能改变,常量指针可以指向其他的地址。
3、指针常量指向的地址不能改变,但是地址中保存的数值是可以改变的,可以通过其他指向改地址的指针来修改。
4、区分常量指针和指针常量的关键就在于星号的位置,我们以星号为分界线,如果const在星号的左边,则为常量指针,如果const在星号的右边则为指针常量。如果我们将星号读作‘指针’,将const读作‘常量’的话,内容正好符合。int const * n;是常量指针,int *const n;是指针常量。
5、指向常量的常指针,指针指向的位置不能改变并且也不能通过这个指针改变变量的值,但是依然可以通过其他的普通指针改变变量的值。

3-修饰函数参数
在给函数传参时,为了防止在函数内部改变形参的值(指针所指向的值),通常给该函数传一个不可改变的指针,这就是所谓的输入型参数。
1、防止修改指针指向的内容

void StringCopy(char *strDestination, const char *strSource);

其中 strSource 是输入参数,strDestination 是输出参数。给 strSource 加上 const 修饰后,如果函数体内的语句试图改动 strSource 的内容,编译器将指出错误。
2、防止修改指针指向的地址

void swap ( int * const p1 , int * const p2 )

指针p1和指针p2指向的地址都不能修改
4-修饰函数的返回值
如果给以“指针传递”方式的函数返回值加 const 修饰,那么函数返回值(即指针)的内容不能被修改,该返回值只能被赋给加const 修饰的同类型指针。
例如函数

const char * GetString(void);

如下语句将出现编译错误:

char *str = GetString();

正确的用法是

const char *str = GetString();

8-宏定义中#、##、#@的区别


#表示:对应变量字符串化 。
##表示:把宏参数名与宏定义代码序列中的标识符连接在一起,形成一个新的标识符。
连接符#@:它将单字符标记符变换为单字符,即加单引号。

@[TOC](9-int *p、int **p、int (*p)()、int *p()、int *p[n]、int (*p)[n])
int *p表示的是一级指针,表示p所指向的地址里面存放的是一个int类型的值。
int **p表示的是二级指针,表示p所指向的地址里面存放的是一个指向int类型的指针。
int (*p)()函数指针
int *p()代表的是调用这个函数后,这个函数的返回值是一个指针。指针函数。
int *p[n] 就是一个指针数组,数据类型为int。指针数组。
int (*p)[n]表示定义一个指针,指向一个int[n]型的指针。数组指针

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值