C程序设计基础(1):基本程序框架和Format IO (printf、scanf)

  本篇文章作为C语言程序设计基础的第一节,将以C语言为载体展开丰富精彩的编程世界。为了运行一个C语言程序,我们首先要构建一个基本的程序框架。为了实现程序与用户(测试服务器)之间的信息交换,我们还需要学习一些基本的Format I/O(格式化输入/输出)语句。文章由浅入深地介绍了输入输出的相关语法和参数,部分内容在实际应用中不一定会涉及,可以仅作为了解。当然,相关语法和程序设计思想的熟练需要在学习的同时亲身实践以增加印象,否则理论脱离实践将失去价值。希望读者能够以边学边用的实用型方法学习程序设计。

C语言程序设计(1)

基本程序框架

 (主)函数

  C语言程序编译时,将首先搜索main主函数,并将其作为程序的开始函数。程序的其他所有函数将直接或间接地通过主函数调用。在一个程序中,main主函数必须有且仅有一个。说到函数,先来看一看函数的基本构成

int main(int argc, char* args[])
{
    
    
    
    return (0);
}

其中,

  • int: 函数的返回值类型(这里为int整型),与变量类型定义相同,将在下面说到。
  • main:函数名称,可自己定义。
  • (int argc, char* args[])参数位置,可以另外定义,这里是main函数带的程序执行参数(在命令行中使用),一般情况下main函数不需要带参数,直接()即可。在需要传递参数的函数中可以自己定义参数,格式与变量声明相同,参数之间用逗号隔开。
  • return: 函数返回。在某些需要返回值的函数中,在return语句后加上的数据将作为函数返回值返回。main函数中,返回的是程序的返回值。一般约定,程序正确执行,将返回0;发生某种错误时返回值将特别规定。main一般返回非0值时代表程序未能正确运行完成,在部分oj中可能发生测试错误导致不通过。B&R C建议,将返回值用括号包含。
  • 函数体必须要用花括号{}包含。在其他主体为多个语句的语句(循环语句等)中,如果不使用花括号,编译器将认为该函数只包含一个语句
  • 有关函数的更多内容,将在后面给出。

#include文件引用

  #include将其他文件定义的函数、常量定义等加入程序文件中。引用这些文件,就可以使用这些文件中的相关函数或常量。

#include <stdio.h>

引用stdio.h头文件,就可以使用标准输入输出函数printfscanf等;引用string.h头文件,就可以使用内存控制函数memset memcopy等。

关于#include<>""的区别<>中引用的是C标准库头文件,""中引用的一般为用户自定义头文件。若使用<>,将在C标准库目录中搜索对应文件。若使用<>,将现在当前目录中搜索头文件,如果找不到文件,再到标准库目录中搜索。

全局/局部 变量声明

  在函数内声明的变量为局部变量(即使在main函数中也是局部),不能在函数以外的范围内使用。如果要声明一个在整个程序中都能使用的变量,就需要声明一个全局变量。

  • 优点:能够在所有函数中使用,方便需要多点使用的变量。
  • 缺点:在程序执行过程中无法回收内存导致占用过多;局部变量与全局变量重名时程序可能难以理解;函数调用另一个函数时如果跟使用相同变量可能会引发错误。

基本程序框架

#include <stdio.h>
#include <string.h>

int a, b;
double c, d;

int handle(int number) //a function
{
    
    
    int a = number;
    //do something
    return (a);
}

int main()
{
    
    
    int num = 666;
    handle(num); //call a function named "handle"
    //do something
    return (0);
}

**注意:函数声明需要在调用它的函数之前,即使主体不在前面也应该在前面提前声明。**具体内容将在函数部分给出。

该样例中a b c d 为全局变量,在handlemain函数中均可使用,num为局部变量,只能在main函数中使用。


printf 格式化输出

printf格式化输出,主要含有两类参数。第一个为必选的格式字符串,后面的不定参数组为格式字符串中转换说明符对应的变量。

样例:

#include <stdio.h>

int main()
{
    
    
    int a = 1, b = 2;
    printf("Hello World.\n");
    printf("%d,%d", a, b);
    printf("%d\n%d\n", a, b);
    return 0;
}

样例包含了三组输出:

  1. 输出了一行"Hello World."这一行没有转换说明,不需要任何附加的参数;
  2. 输出了一行以逗号,分隔的两个变量a b,以十进制整数形式输出。这里使用了两个转换说明,在后面按照顺序给出;
  3. 输出了两行数字,以十进制整数形式,中间增加了一个换行符\n,是转义符中的一个

格式字符串

格式字符串中主要包含三种字符:

  • 普通字符:就是普通的字符,按照原样输出到终端中;
  • 转换说明符:是格式字符串中的重要组成部分,以%开头,以代表转换类型的字符结尾。将后面跟着的参数组数据信息转换成对应格式进行输出;
  • 转义符:以反斜杠\开头的字符(仅包含\和其后面一个字符),表示特殊转义作用

附加参数组

附加参数组跟在格式字符串后面,数量与格式字符串中的转换说明*(包含最小字段宽度指定符)*相同,按照格式字符串中的前后顺序给出

转换说明符

转换说明包含三个部分

  1. %转换说明的开头,表示后面的一串(到类型指示符为止)字符是一个数据转换,将后面提供的对应位置的参数转换为相应格式后输出
  2. 中间一部分是转换说明修饰符,定义了输出的具体格式
  3. 最后一部分是转换说明类型指示符,指定了这个数据将以何种类型输出到终端(与后面提供参数数据的类型无关

转换说明类型指示符

指示符 输出格式 样例
d or i 有符号十进制整数 392
u 无符号十进制整数 7235
o 无符号八进制整数 610
x 无符号十六进制整数 7fa
X 无符号十六进制整数 7FA
f 十进制浮点数(单精度) 1.3800
lf 十进制浮点数(双精度) 1.3800
e 科学计数法,小写 3.96e+2
E 科学计数法,大写 3.96E+2
g %e和%f中的最短表示式 1.38
G %e中的最短表示式 1.38
a 十六进制浮点数,小写形式
A 十六进制浮点数,大写形式
c 单个字符 a
s 字符串 hello
p 指针地址 b8000000
n 不进行任何输出, 与之对应的参数必须是一个指向有符号整型变量的指针; 函数执行结果是将至今已经写入的字符数目写入该指针指向的变量(内存中)
% %%将会输出一个%

样例:(仅写出格式字符串和参数内容,具体输出看后面注释)

("%d", 12);  //以十进制输出 12
("%o", 12); //将十进制数12转为八进制输出 14
("%f", 1.0); //输出一个浮点数 1.000000
("%e", 1.0); //以科学计数法形式输出一个浮点数 1.0000000e+00
("%a", 20.5); //以十六进制浮点数形式输出一个浮点数 1.4800000000000p+4;

各个转换说明的具体显示类型可以自己实验得到,这里不再赘述。

转换说明修饰符

转换说明修饰符在"%"和转换字符之间插入,可修饰基本的转换说明,多个插入时应注意顺序与表格中相同

修饰符 说明
标记 见下表printf中的标记
数字 最小字段宽度(如果这一段可以容纳待打印量,则该字段宽度为设定的最小字段宽度,如果无法容纳, 该字段宽度将增大),若在前使用前导0,则前面使用0(与整型的.相同),若指定整数精度,则该修饰符将被忽略
. 精度(一般后接数字) "%e%E%f"转换,表示小数点后的数字位数 如果浮点数小数位长于标注,则四舍六入五成双 若为0,则可以省去 "%g%G"转换,表示有效数字的最大位数 "%s"转换,表示字符串的最大长度(strlen()) 对于整型转换,表示待打印数字的最小位数,若必要,则使用前导零
h 和整型转换一起使用,表示 (unsigned) short int类型值
hh 和整型转换一起使用,表示 (un)signed char类型值
j 和整型转换一起使用,表示intmax_tuintmax_t类型的值,在stdint.h中定义
l 和整型转换一起使用,表示 (unsigned) long int类型的值
ll 和整型转换一起使用,表示 (unsigned) long long int类型的值
t 和整型转换一起使用,表示ptrdiff_t类型的值,表示两个指针差值
z 和整型转换一起使用,表示size_t类型的值,是sizeof返回的类型
L 和浮点类型转换一起使用,表示long double 类型的值
  • 由于sizeof语句返回的类型在不同实现中可以是任何unsigned修饰的整型,C提供了可移植性更好的类型size_tt表示底层类型,同样,C定义了ptrdiff_t地址差值的底层类型
标记 说明
- 打印项左对齐(默认右对齐)
+ 对有符号值有效,若为正,加上+,为负-
(space) 对有符号值有效,若为正,前面加一空格,为负-(不加+并能保持对齐)
# 为特殊进制数字加前导(0x, 0(八进制)等)
0 在最小列宽之前,对于数值格式,将使用前导零填充列宽

例如:

("ull", a); //表示输出一个"unsigned long long"类型的数据
("%-7.3f", 1.0); //表示输出一个右对齐的,最小字段宽度为7的,小数位后有三位的十进制浮点数 "  1.000" 

转义符

以反斜杠\开头的字符(仅包含\和其后面一个字符),表示特殊转义作用

转义符 说明
\n 换行符
\r 回车符(在linuxios系统中用到)
\t Tab制表符
\\ 一个反斜杠\
  • 其中\n换行符最常用,一般要在程序输出的末尾加上一个换行符

scanf格式化输入

scanf的格式字符串作用与printf中的类似,将数据按照格式字符串中的顺序输入。

转换说明类型指示符

scanf命令的转换说明类型指示符与printf的大致相同,下面给出了其特殊的转换说明

特殊转换说明 说明
%c 将输入解释为一个字符(仅单个),包含空白字符(回车,空格,Tab)

转换说明修饰符

转换说明修饰符与prinf中的有一点差异,

修饰符 说明
* 抑制赋值
数字 最大字段宽度,输入达到最大字段宽度,或者第一次遇到空白字符时停止输入
hh 将整数作为(un)signed char 读取,同printf
ll 将整数作为(unsigned) long long读取
h、l、L “%hd” "%hi"同上 “%ho” “%hx” "%hu"表明将值存储为unsigned short int类型 “%ld” "%li"表明把对应的值存储为long “%lo” “%lx” “%lu” unsigned long “%le” “%lf” “%lg” double,(普通为float) 在efg前用L替换l,表明将对应的值存储为long double 类型
j 在整型转换说明后,表明使用intmax_t或uintmax_t类型
z 整型转换说明后,表明使用sizeof返回类型 %zd
t 在整型转换说明后,表明使用表示两个指针差值的类型ptrdiff_t

说明&注意

  • 格式化输入中两个变量中加不加空格结果相同,即"%d%d"与"%d %d"相同,接收所有空字符(space,enter,tab等)

  • 格式化输入字符串最后的一个空格用于接收除了空字符以外的所有字符(若是数字则必须分开)

  • 如果两个变量中间的字符为非空字符,则必须按照非空字符一对一接收

  • 对于字符串"%s",scanf将在读取到第一个空白字符处停止,还有其他输入函数如fgets()提供另外功能

  • 除了"%c",其余所有转换说明都会跳过开头的空白字符

  • 在读取整数时,如果一开始就读取到非数字非空白字符,函数会将这个数字放回输入中(即下个字符的读取从这里开始),并不会给这个变量输入任何值

  • 如果读取中存在多个转换说明,如果发生读取错误,函数将会停止在第一个错误发生的位置,即不会给任何后面的变量读入赋值

  • scanf的返回值表示读到的项数,如果没有读取任何项,需要读取一个数字而用户输入了一个非数值字符串,scanf的返回值就为0

  • 对于文件读写,当scanf读取到文件末尾时,将返回一个EOF,为宏定义常量,=-1


Format I/O 相关注意事项

关于I/O中修饰符*的使用

  • 输出printf函数中,*表示替换的字段宽度(包含最大和最小),这个修饰符需要按照顺序给出对应的整型变量,这让字段宽度可以动态定义
  • 输入scanf函数中,*表示按找转换说明的格式要求读取,但是不赋值给变量,相当于跳过一个值

以下介绍了转换说明和变量类型不同时发生的情况及其机理,强烈建议不要使用不匹配的类型转换说明

不匹配的整数类型转换

  • 参考前一章对计算机数据处理的讲解,当转换说明于待打印数据类型不匹配,将不会出现致命错误,而是将返回一个奇怪的值。由于计算机对有无符号位数字的错误判断将导致显示异常,有符号型数字转换为无符号型,将返回一个正的较大数;无符号型数字转换为有符号型,如果数字较大,将返回一个负数。
  • 对于存储位数不同的数据,将大数据错置到小位数变量中,将会发生截断,将会得到对变量支持位数那个位置截断的后段数据,等同取模运算

不匹配的浮点类型转换

  • 如果对一个4位数据(int、long)浮点化输出,printf()将其视为double(8位),读取该数据的位置所在数据,四位不能满足要求,所以继续向前读取相邻位上的数据,所得结果无意义
  • 即使数据位数正确,整型数也不能与浮点数相互转换输出,由于显示原理不同,得到结果仍然是无意义的

猜你喜欢

转载自blog.csdn.net/Zheng__Huang/article/details/109152784