【编程语言】C语言基础(包括:关键字、数据类型、输入输出)

程序中的变与不变——变量与常量

标识符和关键字

标识符:用来标识程序中用到的变量名、函数名、类型名、数组名、文件名以及符号常量名的有效字符序列。

语法规则(部分):

  • 标识符只能是由英文字母、数字和下划线组成的字符串,并且第一个字符必须为字母或下划线;
  • 标准C语言不限制标识符的长度,但是受到各个版本的C语言编译系统的限制。大多数情况下,标识符的前8位有效,也就是说,如果两个标识符的前8位相同,系统将会认为是同一个标识符。

关键字:C语言规定的具有特殊意义的字符串,通常也称为保留字(37个)。通常分为:数据类型关键字、语句控制关键字、存储类型关键字、其他关键字。

下面列出了C语言的所有关键字:

数据类型关键字
char double enum float
int long short signed
struct union unsigned void
语句控制关键字
for do while break
continue if else goto
swith case default return
存储类型关键字
auto extern register static
其他关键字
const sizeof typedof volatile
inline restrict _bool _complex
_imaginary      

常量和变量

在程序执行过程中,其值不发生改变的值称为常量,其值可变的称为变量。在程序中,变量通常是可以不经说明而直接引用的,而变量则必须先定义后使用。

常量可以分为:数值常量、字符常量、字符串常量和符号常量等。

  • 数值常量:数值常量通常表示的是数字。
int a=1;

在这句程序中的1,在程序运行过程中是始终不变的,是常量。

  • 字符常量:字符常量通常是指单引号里的单个字符。
int a=1;
int b='1';

在这个程序段中,1和'1'的含义是不一样的,一个是数值,可运算;一个是字符,仅仅是一个符号而已。

  • 字符串常量:字符串常量通常是指双引号里的字符串。

这里需要注意一种特殊情况,比如“\n”、“\a”,像这种字符常量就是通常所说的转义字符。这种字符以反斜杠(\)开头,后面跟一个字符或者一个八进制或者十六进制数,表示的不是双引号里面的值,而是“转义”。

下面列出了C语言的所有转义字符:

常见转义字符
字符形式 含义 字符形式 含义
\x20 空字符 \f 换页符
\n 换行符 \' 单引号
\r 回车符 \" 双引号
\t 水平制表符 \\ 反斜杠
\v 垂直制表符 \? 问号字符
\a 响铃 \ddd 任意字符
\b 退格符 \xhh 任意字符
  • 符号常量:符号常量是使用一个标识符来表示一个常量。但是,符号常量在使用前必须要先定义,定义形式为:
#define 标识符 常量

其中,#define是一条预处理命令,称为宏定义命令。在编译之前,系统会自动把所有的标识符都替换成常量。

变量按其作用域分,可分为局部变量和全局变量,具体的解释为:

  • 全局变量(外部变量):在所有源文件内均有效。在同源文件的函数中使用使用全局变量,需要将全局变量提前声明;同时在不包含全局变量定义的不同源文件需要用extern关键字再次声明。
  • 静态全局变量:只在定义它的源文件内有效;
  • 静态局部变量:只在定义它的函数内有效。只是程序仅分配一次内存,函数返回后,该变量不会消失,只有程序结束后才会释放内存;
  • 局部变量(内部变量):在定义它的函数内有效,但是函数返回后失效。

关于C语言变量,还有几个注意点:

  • 由于C语言规定函数的返回值只能有一个,当需要增加函数的返回数据的时候,使用外部变量是一种很好的方式。因此,外部变量是实现函数之间数据通信的有效手段。
  • 变量的定义是给变量分配内存空间,在分配好内存空间后,程序没有运行前,会给变量分配一个不可知的混乱值,如果程序中没有对其进行赋值就使用的话,势必会引起不可预期的结果。所以,变量在使用前必须进行初始化。
关于C语言变量的详细讲解,可参考链接 【编程语言】C语言函数中的变量(包括:作用域、存储类型)


数据的种类——数据类型

所谓数据类型就是按被说明量的性质、表现形式、占据存储空间的大小、构造特点来划分的。在C语言中,数据类型可分成基本数据类型、构造数据类型、指针数据类型、空类型四大类。本文主要介绍基本数据类型。

数制

  • 二进制:二进制是逢二进一的数制,目前的计算机全部是采用二进制系统;
  • 八进制:八进制是逢八进一的数制,八进制数必须以0开头,即以0作为八进制的前缀。八进制数通常是无符号数;
  • 十六进制:十六进制就是逢十六进一的数制,十六进制数必须以0x开头。

数制间的转换:标准输出函数printf()可以用于函数输出转换。

printf()函数的格式控制参数
格式控制参数 描述
%d 十进制有符号整数

%u

十进制无符号整数
%f 十进制浮点数
%o 八进制数
%x 十六进制数

例如,将unsigned int类型的数12进行十进制、八进制、十六进制的输出:

unsigned int x=12;
printf("十进制%u 转换为八进制%o 转换为十六进制%x\n",x,x,x);

基本数据类型

short、int、long、char、float、double 这六个关键字代表C 语言里的六种基本数据类型。

在计算机内部的数据都是以2进制的形式存储的,每一个二进制数称为1位(bit),位是计算机中的最小的存储单元,一个8bit的二进制数称为一个字节(Byte),不同的数据类型有不同的字节要求来存储。但是,各种无符号类型量所占用的内存空间与相应的有符号类型量相同。正是由于省去了符号位,故无符号数不能表示负数,使得无符号数可以存放的正整数的范围比有符号数扩大了一倍。

在不同的系统上,这些类型占据的字节长度是不同的:

C语言数据类型
类型说明符 比特数(字节数)(32位系统) 比特数(字节数)(64位系统)
short(短整型) 16(2) 16(2)
int(整形) 32(4) 32(4)
long(长整型) 32(4) 64(8)
float(单精度浮点数) 32(4) 32(4)
double(双精度浮点数) 64(8) 64(8)
char 8(1) 8(1)

除了这些之外,还有一些特殊的数据类型:

C语言数据类型
类型说明符 比特数(字节数)(32位系统) 比特数(字节数)(64位系统) 备注
char * 32(4) 64(8) 其他指针类型如long *, int * 也是如此
long long 64(8) 64(8)  
long double 80(10)/96(12) 80(10)/128(16) 有效位10字节。32位为了对齐实际分配12字节;64位分配16字节

通过上表,可以看出,对于32位和64位机器,只有long和指针类型的长度不一样,其它类型所占字节数都是一样的(long double除外,见备注)。

解释:在MSDN上关于这一点有描述,On 32-bit platforms, long is synonymous with int.

即:在32位机上,long与int同义。

不同的编译器会分配不同的空间,还跟计算机是多少位的有关,不过一般long是int的两倍长度。long的定义是其所占用的存储空间大小不小于int。long 和int只是(可能会)存储空间大小不同,而读取两者中的数据时使用的方法规则是一样的。而在32位机上,两者是相同的。

整型和浮点型

整形在内存中是以二进制的形式存放的。如果定义了一个整形变量i:

int i=10;

在内存中变量i的实际存放是:00000000 00000000 00000000 00001010。

数值是按照补码的形式表示的:

  • 正数的补码与源码相同;
  • 负数的补码等于该数绝对值的二进制形式按位取反再加1。

浮点型在内存一般是按照指数形式存储的。如果定义了一个浮点数变量j:

float j=3.14159;

在内存中变量j的实际存放是:符号位,小数部分(.314159),指数部分(1)。

  • 小数部分占位越多,数的有效数字越多,精度越高;
  • 指数部分占位越多,数的取值范围越大。

字符型

字符变量在内存中如何存储:

字符是系统中字符集中的一系列的符号,在使用的时候,用编号进行读取,也就是说在字符变量里面放着这些符号的编号,这些编号就是ASCII码。ASCII码是一些整形的数据,这也造成了字符型变量可以在一定程度上可以和整形进行换算。因为数字1-9、字母a-z、字母A-Z的ASCII码都是连续的,而且是整形。

字符串型

字符串常量和字符常量的区别:

  • 字符常量是单引号括起来的,字符串常量是双引号括起来的;
  • 字符常量只能是单个字符,字符串常量可以是单个或多个字符;
  • 可以将一个字符常量赋值给一个字符变量,但是不能讲一个字符串常量赋值给一个字符串变量的。

因为C语言中是没有字符串变量这个说法的!但是,可以用一个字符数组来存放一个字符串常量。

  • 字符常量占用1个字节的内存空间,字符串常量占用的内存字节数等于字符串中字节数加1。

因为增加的1个字节的空间是用来存放字符“\0”(ASCII码为0)。这是字符串结束的标志。

数据类型的转换

隐式转换


隐式转换,又称为自动转换,应遵循以下规则:

  • 若参与运算的类型不同,则先转换成同一类型,然后进行计算;
  • 转换按数据长度增加的方向进行,以保证精度不降低;
  • 所有的浮点数运算都是以双精度进行的,即使仅含有float单精度量运算的表达式,也要转换成double;
  • char和short参与运算时,必须先转换成int;
  • 赋值运算时,赋值号左右两边的数据类型不同时,赋值号右边的类型将转换成左边的类型,即使丢失精度。

显式转换

如果隐式转换不符合要求,非常明确地希望转换数据类型,可以使用显示转换。其一般形式是:

(类型说明符) (表达式)

无论是隐式转换还是显示转换,都只是为了本次运算的需要而对变量的数据长度进行的临时性转换,而不改变数据说明时对该变量定义的类型。


用户与计算机的交互——输入和输出

输入/输出函数一览
函数名称 描述
printf 标准化输出
scanf(scanf_s 标准化输入
getchar 字符输入
putchar 字符输出
gets 字符串输入
puts 字符串输出

这些函数的具体描述和讲解如下:

格式化输入/输出

C语言提供了许多的常用函数(包括输入/输出函数、数学运算函数等),他们被存放在系统的函数库中。在C语言的标准库stdio.h文件中,提供了一些通用的函数(如printf()函数和scanf()函数等),这些函数被各种计算机系统所提供,因此也是标准输入/输出函数。

使用标准输入/输出库函数的时候需要用到stdio.h文件,因此源文件开头应有相应的预编译命令:

#include<stdio.h>
#include "stdio.h"

stdio是standard input&output的意思。

格式化是指按照一定的格式,格式化输入/输出是指按照一定的格式读取来自输入设备的数据或者向输出设备输出数据。符合格式化输入/输出的代表函数就是printf()函数和scanf()函数。

  • 格式化输出函数——printf()函数

printf()函数调用的一般形式如下:

printf("格式控制字符串",输出表列);

其中,格式控制字符串用于指定输出格式,由格式字符串和非格式字符串组成。格式字符串是以%开头的字符串,在%后面根由各种格式的字符,已说明输出数据的类型、形式、长度、小数位数等。

格式控制字符的一般形式如下:

%[*] [标志][输出最小宽度][.精度][长度]类型

其中,方括号[]代表的是可选项。其中各项的描述介绍如下:

  • 类型:类型字符用于表示输出数据的类型,其格式字符和描述如下:
格式控制字符的类型
格式字符 描述
%d 以十进制形式输出带符号整数(正数不输出符号)
%o 以八进制形式输出无符号整数(不输出前缀0)
%x、%X 以十六进制形式输出无符号整数(不输出前缀0x)
%u 以十进制形式输出无符号整数
%f 以小数形式输出单、双精度实数
%e、%E 以指数形式输出单、双精度实数
%g、%G 以%f和%e中较短的输出宽度输出单、双精度实数
%i 与%d相同
%c 输出单个字符
%s 输出字符串
%p 输出指针
%% 输出一个百分号
  • 标志:标志参数表示输出数据的一些特征,而不影响数据的值,其标志字符和描述如下:
格式控制字符的标志
标志字符 描述
- 结果左对齐,右边填空格
+ 输出符号(正号或负号)
空格 输出值为正时冠以空格,为负时冠以负号
#

对%c、%s、%d、%u无影响;对%o,加上前缀o;

对%x,加上前缀0x

  • 最小输出宽度:用十进制整数来表示输出的最小位数。若实际位数大于定义的宽度,则按照实际位数输出;若实际位数小于定义的宽度,则补以空格或0;
  • 精度:精度格式符以“.”开头,后跟十进制整数。此项的意义是如果输出的是数字,则表示小数的位数;若输出的是字符,则表示输出字符的个数;
  • 长度:长度格式符分为h、l两种,h表示按短整形量(short)输出,l表示按长整形量(long)输出;
  • *:*是用来专门代替最小输出宽度、精度的,此时需要在参数列表中多对这个符号进行赋值。

例如:

#include<stdio.h>

int main()
{
	float b = 123.1234567;
	int a = 88;

	printf("a=%d,%5d,%o,%x\n", a, a, a, a);
	printf("b=%f,%lf,%5.4lf,%e\n", b, b, b, b);
	printf("%c\n", a);

	return 0;
}

这段程序运行的结果是:

a=88, 88,130,58
b=123.123459,123.123459,123.1235,1.231235e+02
X
请按任意键继续. . .
  • 格式化输入函数——scanf()函数

scanf()函数调用的一般形式如下:

scanf("格式控制字符串",地址表列);

其中,格式控制字符串的作用与printf()函数相同,不能显示非格式字符串。地址表列中给出个变量的地址。地址是由地址运算符“&”后跟变量名组成的。

格式控制字符串的一般形式为:

%[*][输入数据宽度][长度]类型

其中,方括号[]代表的是可选项。其中各项的描述介绍如下:

  • 类型:类型字符用于表示输入数据的类型,其格式字符和描述如下:
格式控制字符的类型
格式字符 描述
%d 输入十进制整数
%i 与%d的作用相同
%o 输入八进制整数
%x或%X 输入十六进制整数
%u 输入无符号十进制整数
%f或%e 输入实数(用小数形式或指数形式)
%c 输入单个字符
%p 输入一个指针(地址)
%s

输入字符串:输入的内容以第一个非空白字符开始,到下

一个空白字符结束

  • *:*表示该输入值,输入后不赋予相关的变量,即跳过该输入值;
  • 输入数据宽度:用十进制整数指定输入的宽度,即输入字符的个数;
  • 长度:长度格式符分为h、l两种,h表示按短整形量(short)输入,l表示按长整形量(long)输入。

但是需要注意的是,现在由于scanf()函数的不安全性,重新定义了一个带“_s”后缀的函数是为了让原版函数更安全,传入一个和参数有关的大小值,避免引用到不存在的元素,有时黑客可以利用原版的不安全性黑掉系统。也就是scanf_s()函数,它的函数一般形式如下:

scanf_s("格式控制字符串",地址表列,地址表列的大小);

例如:

#include<stdio.h>

int main()
{
	char c[10];
	int a, b;
	char d, e;

	scanf_s("%d%d", &a, &b);
	printf("a=%d\nb=%d\n", a, b);

	scanf_s("%s", &c, 10);
	printf("c=%s\n", c);

	scanf_s("%c%c", &d, 1, &e, 1);
	printf("c=%c\nd=%c\n", d, e);

	return 0;

}

这段程序运行的结果是:

123 456
a=123
hello world
b=456
d=w
c=hello
c=
请按任意键继续. . .

从上面的程序中可以看出,以下几个注意点:

  • scanf_s()函数在输入多个数值数据时,若格式控制字符串中没有非格式字符作为输入数据之间的间隔,则可用空格、Tab或者回车键作为间隔。C编译在碰到空格、Tab、回车或非法数据(如对“%d”输入“12A”时,A就是非法数据)时,即认为该数据结束。此时函数不需要提供地址列表的大小!
  • scanf_s()函数输入字符串时,输入内容以第一个非空白字符开始,到下一个空白字符结束,即空格就代表着字符串的结束。此时函数需要提供地址列表的大小!
  • scanf_s()函数在输入字符数据时,若格式控制字符串中无非格式字符作为输入数据之间的间隔,则认为所有输入的字符均为有效字符,即空格也是可以被输入的。此时函数需要提供地址列表的大小,也就是1!

例如,做一个整数计算器的输入(代码部分):

#include<stdio.h>

int main() 
{
	int num1, num2;
	char operation;

	scanf_s("%d%c%d", &num1, &operation, 1, &num2);
	printf("%d%c%d\n", num1, operation, num2);

	return 0;
}

这段程序的scanf_s的输入部分,整数%d不需要地址列表的大小,字符%c需要地址列表的大小(1)。

但是,如果格式控制字符串中有非格式字符作为输入数据之间的间隔,那么就要按照该分隔符进行输入。

字符输入/输出

在C语言中,字符的输入/输出是程序经常进行的操作,频率比较高,所以C语言库函数中专门设置了putchar()和getchar()函数用于对字符的输入输出进行控制。

  • 字符输出函数——putchar()

putchar()函数是字符输出函数,其功能是在显示器上输出单个字符。在使用它之前,必须要先包含头文件stdio.h。

putchar()函数调用的一般形式为:

putchar(字符变量);

同时,它也能进行一些控制字符的输出。例如:putchar("\n")。

  • 字符输入函数——getchar()

getchar()函数是字符输入函数,其功能是从键盘上输入一个字符。在使用它之前,必须要先包含头文件stdio.h。

getchar()函数调用的一般形式为:

char c=getchar();

注意:getchar()函数只能接受单个字符,如果输入多于一个字符,则只接受第一个字符。

	putchar(getchar());

将两个函数合并使用。

字符串输入/输出

在C语言的各种应用中,和字符串的操作相比,字符的使用相对要少很多。所以C语言库函数中专门设置了puts()和gets()函数用于对字符串的输入输出进行控制。

  • 字符串输出函数——puts()

puts()函数是字符串输出函数,其功能是在显示器上输出字符串。在使用它之前,必须要先包含头文件stdio.h。

puts()函数调用的一般形式为:

puts(指向字符串的指针);

由于C语言中没有字符串变量这个说法,也就是说这里通常使用的是:

#include<stdio.h>

int main()
{
	char str[] = "Hello";
	char* f;
	f = "World";

	puts(str);
	puts(f);

	return 0;

}

这段程序运行的结果是:

Hello
World
请按任意键继续. . .

也就是,如果是字符数组,可以直接做puts()函数的参数;如果是字符串常量,需要用字符指针来作参数。

  • 字符串输入函数——gets()

gets()函数是字符串输入函数,其功能是从键盘上输入字符串,直到回车结束。在使用它之前,必须要先包含头文件stdio.h。

gets()函数调用的一般形式为:

gets(指向字符串的指针);

由于C语言中没有字符串变量这个说法,也就是说这里通常使用的是:

#include<stdio.h>

int main()
{
	char str[20];
	char* f;

	gets(str);
	f = gets(str);

	return 0;

}

这里有几点需要注意:

  • 当需要进行字符串输入的时候,如果scanf()配合%s使用,但是这种方法只能获取一个单词,即遇到空格等空字符就会返回。而gets()函数正好可以解决这个问题。
  • gets()函数从标准输入(键盘)读入一行数据,所谓读取一行,就是遇到换行符就返回。gets()函数并不读取换行符'\n',它会把换行符替换成空字符'\0',作为c语言字符串结束的标志。
  • gets()函数经常和puts()函数配对使用,puts()函数用于显示字符串,并自动在字符串后面添加一个换行标志'\n'。
char str[10];
puts(gets(str));

将两个函数合并使用。

gets()函数存在一个严重的缺陷,这个缺陷就是:它不会检查数组是否能够装得下输入行。

如果输入的字符串过长,会导致缓冲区溢出(buffer overflow),即多余的字符超出了指定的目标空间。gets()函数就会访问未被分配的内存空间,如果这些内存未被使用,不会立即出现问题;如果这片空间已经存有数据,就会擦除掉程序中的其他数据,会导致程序中止。

正式由于gets()函数的这个缺陷,在C99标准中,已经不再建议使用gets()函数,而在C11中更是直接抛弃了这个函数。然而在实际应用中,编译器为了兼容以前的代码,大部分都继续支持gets()函数。

gets()被抛弃,那我们用什么来代替它的功能呢?C11标准新增了gets_s()函数可以代替gets()函数。

猜你喜欢

转载自blog.csdn.net/qq_38410730/article/details/80138714
今日推荐