C语言知识-零零散散(三)

C语言知识-零零散散(三)

while循环中的scanf函数

首先给出下面的源码:

#include<stdio.h>
#include<stdlib.h>

int main()
{
	char c = 0; 
	int i=0;

	while (1)
	{
		scanf("%c", &c);
		i++;
		printf("i = %d\n", i);
		if(i == 5){
			break;
		}
	}
	system("pause");
	
	return 0;
}

程序运行以后,如果我们在键盘上输入"abcd",然后敲回车,会出现什么样的现象呢?结果如下:
在这里插入图片描述
为什么会出现这样的现象呢?scanf不是应该只接收字母’a’吗,为什么变量i直接累加到5了?原因是,scanf在接收到了换行符以后,结束了此次的输入,并且把变量c的值赋值为’a’。但是,"bcd"加上回车这些字符并没有被丢弃,而是遗留在输入缓冲区,当需要再次的调用scanf函数时,这些遗留的值就自动的充当输入字符。所以,我们输入了"abcd"四个字符和一个回车,总共五个字符,使得i=5,程序直接结束了。可以利用下面的代码验证一下:

int main()      
{
	char c = 0; 
	int i=0;

	while (1)
	{
		scanf("%c", &c);
		i++;
		printf("i = %d\n", i);
		printf("the ASCII of %c is %d \n", c, c);
		if(i == 5){
			break;
		}
	}
	system("pause");
	
	return 0;
}

这样就会造成一个问题,我们的本意是输入一个字符,变量i自动加一,现在是输入一个字符,i自动加了两次。解决这个问题可以使用fflush函数,来清空输入缓冲区中的所有数据。如下面代码:

#include<stdio.h>
#include<stdlib.h>

int main()
{
	char c = 0; 
	int i=0;

	while (1)
	{
		scanf("%c", &c);
		fflush(stdin);
		i++;
		printf("i = %d\n", i);
		if(i == 5){
			break;
		}
	}
	system("pause");
	
	return 0;
}

这样操作以后,不过我们键入多长的字符,每次scanf只接收一个,达到我们本来的目的。
在这里插入图片描述

return和exit()的区别?

  • return是关键字,exit是一个函数
  • return表示了调用堆栈的返回,是一个函数的退出;exit表示一个进程的结束,是一个进程的退出
  • return用于结束一个函数的执行,将函数的执行信息传出个其他调用函数使用;exit函数是退出应用程序,删除进程使用的内存空间,并将应用程序的一个状态返回给OS(操作系统),这个状态标识了应用程序的一些运行信息,这个信息和机器和操作系统有关,一般是 0 为正常退出,非0 为非正常退出
  • 非主函数中调用return和exit效果很明显,但是在main函数中调用return和exit的现象就很模糊,多数情况下现象都是一致的。
#include <stdio.h>
#include <stdlib.h>

int func1(int x){
	x++;
	return x;
}

int func2(int x){
	x++;
	exit(0);
	return x;
}

int main(){
	int x = 0;
	char c;
	while(1){
		scanf("%c", &c);
		fflush(stdin);
		if(c == 'q'){
			x = func2(x);
		}
		else{
			x = func1(x);
		}
		printf("x = %d\n", x);
	}

	return 0;
}

代码在运行过程中,如果输入的字符不为’q’,那么就调用func1,执行x自加的操作,并使用return返回,运行代码可以得出,func1返回后,继续执行whlie循环。但是,当我们输入字符’q’时,func2调用exit函数,直接结束当前代码。

形参和实参

形参

形式参数,定义函数名和函数体的时候使用的参数,目的是用来接收调用该函数时传入的参数。

实参

实际参数,在调用函数时,传递给该函数的参数,它们都必须具有确定的值,以便把这些值传送给形参。

形参和实参的区别和联系

  • 形参变量只有在函数被调用时才会分配内存,调用结束后,立刻释放内存,所以形参变量只有在函数内部有效,不能在函数外部使用。
  • 实参可以是常量、变量、表达式、函数等,无论实参是何种类型的数据,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参,所以应该提前用赋值、输入等办法使实参获得确定值。
  • 实参和形参在数量上、类型上、顺序上必须严格一致,否则会发生“类型不匹配”的错误。当然,如果能够进行自动类型转换,或者进行了强制类型转换,那么实参类型也可以不同于形参类型。
  • 函数调用中发生的数据传递是单向的,只能把实参的值传递给形参,而不能把形参的值反向地传递给实参;换句话说,一旦完成数据的传递,实参和形参就再也没有瓜葛了,所以,在函数调用过程中,形参的值发生改变并不会影响实参。
  • 指针变量作函数参数可以将函数外部的地址传递到函数内部,使得在函数内部可以操作函数外部的数据,并且这些数据不会随着函数的结束而被销毁。

函数中变量的作用域

#include <stdio.h>
#include <stdlib.h>

void func(int a, int b){
	a++;
	b++;
	printf("func a = %d\n", a);
	printf("func b = %d\n", b);
}

int main(){
	int a, b;
	a = 1;
	b = 10;
	func(a, b);
	printf("main a = %d\n", a);
	printf("main b = %d\n", b);

	system("pause");
	return 0;
}

代码的输出结果是?

这个代码中,在main函数中定义了a,b变量,在func中也定义了a,b变量,这样并不会冲突,因为它们的作用域不同。在func中的变量a,b表示形参,当调用func时,系统会自动给变量分配内存空间,函数调用结束后,系统回收变量。所以main中的a,b(实参)和func中的a,b(形参)不一样,func中a++,b++也并不会改变main中变量a,b的值。下面我们通过代码分别输出它们的地址来验证分析:

#include <stdlib.h>

void func(int a, int b){
	a++;
	b++;
	printf("func a = %d\n", a);
	printf("func a: %p\n", &a);
	printf("func b = %d\n", b);
	printf("func b: %p\n", &b);
}

int main(){
	int a, b;
	a = 1;
	b = 10;
	func(a, b);
	printf("main a = %d\n", a);
	printf("main a: %p\n", &a);
	printf("main b = %d\n", b);
	printf("main b: %p\n", &b);

	system("pause");
	return 0;
}

输出结果为:
在这里插入图片描述

编写一个简单的mygcc编译器

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
int main(int argc, char* argv[]){
	char arr[100]; // 用于存储gcc命令
	char temp[20]; // 用于分割出函数的名字
	char* p;       // 指向str函数名字的首地址
    
	if(argc < 2){  // 如果参数少于两个,直接退出
		 printf("missing parameters\n");
		 return -1;
	}

	strcpy(arr, "gcc -o ");  // 首先把 "gcc -o "拷贝到arr中
	strcpy(temp,argv[1]);    // 把函数名字拷贝到temp中,用于分割
    p = strtok(temp, ".");   // 分割出函数的名字,p指向函数名字的首地址
    strcat(arr, p);          // 把函数名字追加到arr字符串中
    strcat(arr, " ");        // 把空格追加到arr中
	strcat(arr, argv[1]);    // 把函数名字.c追加到arr中
    // printf("%s\n", arr);  // 测试整个gcc命令拼接是否正确
	system(arr);             // 使用system函数调用命令, 例如"gcc -o test test.c"

     return 0;
}

上面代码可以完成简单的模拟gcc单个文件的编译过程,主要利用main的参数,其中argc表示参数的个数;argv表示传入main函数的参数序列或者指针,argv[0]一定是程序的名称。首先代码判断参数的个数是否小于两个,如果小于表示没有输入要编译的程序的名字,直接返回,如果满足两个,就利用strcpy、strtok、strcat函数来拼接gcc命令。然后我们编译这个代码生产可执行文件,就可以利用这个可执行文件编译其他的c文件了。如

gcc -o mygcc mygcc.c
./mygcc test.c

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/EngineerHe/article/details/107225664