创建第一个C语言程序:快速入门
#include
int main(void)
{
/*我的第一个 C 程序*/
printf("Hello world!!!") ;
retunrn 0 ;
}
/*rearrange.c */
/*
* 此程序从标准输入中读取输入行并在标准输出中打印这些输出行
* 每个输入行后面一行是该行内容的一部分
*
* 输入的第1行是一串列标号,串的最后以一个负数结尾。
* 这些列标号成对出现,说明需要打印的输入行的列的页面
* 这些列标号成对出现,说明需要打印的输入行的列的范围
* 例如,0 3 10 12 -1 表示第0列到第3列 第10列到第12列的内容将被打印
* */
#include
#include
#include
#define MAX_COLS 20 /*所处理的最大列号*/
#define MAX_INPUT 1000 /*每个输入行的最大长度*/
int read_column_numbers(int columns[], int max) ;
void rearrange(char *output, char const *input, int n_columns, int const columns[]) ;
int main(void )
{
setbuf(stdout , NULL) ;
int n_columns ; // 进行处理的列标号
int columns[MAX_COLS] = {0}; //需要处理的函数
char input[MAX_INPUT] = {0}; //容纳需要输入行的数组
char output[MAX_INPUT] = {0}; // 容纳输出行的数组
/*
* 读取该串的列标号
* */
n_columns = read_column_numbers(columns , MAX_COLS) ;
/*
* 读取、处理和打印剩余的输入行
* */
while ( gets(input) != NULL ){
printf("Original input : %s\n", input) ;
rearrange( output , input , n_columns , columns) ;
printf("Rearranged line : %s\n", output) ;
}
return 0 ;
}
/*
* 读取列标号,如果超出规定范围则不予理会
* */
int read_column_numbers(int columns[], int max)
{
int num = 0 ;
int ch ;
/*
* 取得列标号,如果所读取的数小于0则终止
* */
while ( num < max && scanf("%d", &columns[num]) == 1 && columns[num] >= 0 ) {
num += 1;
}
/*
* 确认已经读取的标号为偶数个,因为它们是以对的形式出现的
* */
if (num % 2 != 0){
puts("Last column number is not paired.\n") ;
exit(EXIT_FAILURE) ;
}
/*
* 丢弃该行中包含最后一个数字的那部分内容
* */
while( (ch = getchar()) != EOF && ch != '\n' ) ;
return num ;
}
/*
* 处理输入行,将指定的列的字符连接在一起,输出行一NUL 结尾
* */
void rearrange(char *output, char const *input, int n_columns, int const columns[])
{
int col ; // columns数组的下标
int output_col ; // 输出列计数器
int len ; // 输入行的长度
len = strlen( input ) ;
output_col = 0 ;
/*
* 处理每对列标号
* */
for ( col = 0 ; col < n_columns ; col += 2 )
{
int nchars = columns[col+1] - columns[col] + 1 ;
/*
* 如果输入行结束或输出行数组已满,就结束任务
* */
if ( columns[col] >= len || output_col == MAX_INPUT -1 ){
break;
}
/*
* 复制相关的数据
* */
strncpy(output + output_col , input + columns[col] , nchars) ;
output_col += nchars ;
}
output[output_col] = '\0' ;
}
#include
这个是程序的第一行,作用为在我们的代码中该行所在的位置引入stdio.h这个文件的全部内容,更简单的说,就像一个粘贴的操作,而此刻大家应该想到,#include后尖括号<>里的文件也可以是其他头文件,没错,在今后的学习中,我们会看到更多的#include语句,其中包含了更多的头文件,这也体现了一种设计思想。可以方便的在多个程序间共享公用的信息。
#include语句是**C预处理器指令(preprocessor directive)**的一个例子,这些是编译器在编译代码之前要做的准备工作,称之为预处理(preprocessing)。
再来说说stdio.h这个文件,不用觉得奇怪,它和我们平常见到的123.txt并无差别,只是后缀名不同而已,这个名字表示:标准输入输出头文件,正如英文:standard input/output header,它包含了与输入输出相关的函数(比如printf)的信息,在C语言的世界中,人们称出现在文件顶部的信息集合为头(header),C实现通常都带有多个头文件。
那么,为什么C语言没有内置的输入输出语句呢?一个答案是并非所有的程序都要用到I/O(输入/输出)包,并且C语言的一个基本的设计原则就是避免不必要的成分。这个经济的使用资源的原则使得C语言在嵌入式编程中非常的流行,例如:为一个控制自动报警系统的芯片编写程序。值得一提的是,#include甚至不是C语言的语句!开头的#,这个符号表明这一行是在编译器接手之前先由C预处理器处理的语句。以后我们将碰到更多的预处理指令。
提示
在以后的C开发中,往往需要很多的C文件,所以有效的使用预处理指令可以帮助我们解决很多问题。
int main()
{}
这几行的代码声明了一个main函数。。一个C程序(我们不考虑例外的情况)总是从main()函数开始执行。我们可以对自己使用的其他函数任意命名,但是main()必须是开始的函数。每个C程序中必须有它!
那么圆括号的功能呢?它表明main()是一个函数。以后,我们将遇到更多的函数。现在,请记住这个函数就是C程序的基本模块。
int指明main()函数的返回类型。这意味着main()函数返回值的基本类型为整数。返回到哪里呢?返回给操作系统。为什么要有返回值呢?打个比方,就像皇上交代大臣去办事,大臣完成后总要复命,向皇上禀告,告诉皇上是成功还是失败。
函数名后面的圆括号一般包含传递给函数的信息。关键字void表示函数不接受任何参数。
另外,您可能在一些教材或老的版本中,看到过这样的写法:
main()
这种形式在C90标准中勉强允许这种形式,在C99标准不允许,即使您的编译器允许。
以及还有这样的写法:
void main()
仅仅有些编译器允许这种形式,但还没有任何编译器考虑接受它,而有的编译器则会报错。因此,我们建议您不要这样做。另一方面,坚持使用标准形式,我们也不必担心程序从一个编译环境移到另一个编译环境上时出错的问题。
/*一个简单的C程序*/
空白:空白将程序的不同部分分隔开;制表符(tab)用于缩进语句,便于更好地显示程序的结构。
C是一种自由的格式的语言,并没有特别的要求固定,但是编写C程序时遵守一些约定还是十分值得的。
注释:包含在/* /之间的部分是程序注释。
使用注释的目的是使人们(包括我们自己)更容易的理解我们的程序。
C语言中注释有两种形式:一是:// ; 二是//*
C语言任何地方都可以插入注释,但是注释不能嵌套;
在/和/之间的所有内容都会被编译器忽略掉,如:
int number; /* 定义一个整形变量number*/
注释也可以分成两行或多行,如:
/* 笔者自述:
* 时间:18:24
* 地点:北半球
* 天气:窗外天气不明 微冷
* 肚子有点饿
*/
除此之外,C99还增加另一种风格的注释,它被普遍用在C++或java里,这种新形式使用//符号,但这种注释被限制在一行里,如:
x=10; //将x赋值为10
{ /*... */ }
在C程序中,通常所有的C函数都使用花括号来表示函数体的开始与结束。它们的存在是必不可少的,因此我们不能丢掉它们。并且仅有花括号{}能起到这种作用,小括号()和中括号[]都不行。花括号还可以用来把函数中的语句聚集到一个单元或代码块中。
int num;
程序中这一行叫做声明语句(declaration statement)。该声明语句是C语言中最重要的功能之一。这个特殊的例子声明两件事情。第一,在函数中您有一个名为num的变量,第二,int说明num是一个整数,也就是说这个数没有小数部分(int是C语言的一种数据类型)。编译器使用这个信息为变量num在内存中分配一个合适的存储空间。句末的分号指明这一行是C语言的一个语句或指令,分号是语句的一部分,每个C语言都以一个分号结束。
单词int是C语言的一个关键字,代表了一种C语言的数据类型。关键字是用来表达语言的单词,您不能将他们用于其他目的。比如,不能用int用作一个函数名或者一个变量的名字。
本例中的单词num是一个标识符(identifier),也就是您为一个变量、函数或其他实体所选的名字。这样该声明就把一个特殊的标识符和计算机中的一个特殊位置联系了起来,同时也确定了该位置存储的信息类型(即数据类型),我们为这个变量赋值,也就相当于在计算机中这个特殊的位置(今后我们将清楚,这个位置即所谓的地址)赋值。因为,C语言是人和计算机沟通的纽带。
在C语言中,所有变量都必须在使用之前定义。这就意味着您必须提供程序中要用到的所有变量名的列表,并且指出每个变量的数据类型。声明变量被认为是一种好的编程技术,在C语言当中必须这样做。
传统上,C语言要求必须在一个代码块的开始处声明变量,在这之前不允许任何其他语句。也就是说,main()函数如下所示:
int main() // traditional rules (传统的用法)
{
int year ;
int month ;
int day ;
year = 2014 ;
month = 2;
day = 28; //other statements(其他的语句)
}
现在C99遵循C++的惯例,允许把声明放在代码块中的任何位置。然而,在首次使用变量之前仍然必须先声明它。因此,如果你的编译器支持这种功能,你的代码就可以像下面这样:
int main() //C99 rules(C99 用法)
{
//some statements(一些语句
int doors;
doors = 5; //first use of doors(第一次使用到的变量)
//more statements(更多的语句)
int dogs;
dogs = 3; //first use of dogs(第一次使用到的变量)
//other statements (其他的语句)
}
现在你可能还有三个问题。首先,数据类型是什么?第二,可以选择什么样的名字?第三,为什么必须对变量进行声明?下面来看这些问题的答案。
C语言可以处理多个数据种类(或类型),例如整数、字符和浮点数。把一个变量声明为整数类型或字符类型是计算机正确地存储,获取和解释该数据的基本前提。在后面您将学到各种各样的可用类型。
您应该尽量使用有意义的变量名(例如:如果你的程序需要苹果这个变量,那么使用apple)。如果名字不能表达清楚,可以用注释解释变量所代表的意思。通过这种方式使程序更易读是良好编程的基本技巧之一。
能够使用的字符的数量与C语言的不同实现有关。C99标准允许一个标识符最多可以有 63个字符,除了外部标识符,后者只识别31个字符。与C90分别要求的31个字符和6个字符相比较,这是一个相当可观的进步,而更旧的编译器通常最多只允许8个字符。实际上,你使用的字符数量可以超过规定的最大值,但是编译器不会识别额外的字符。因此,如果一个系统最大字符数为8,那么clangdot和clangdotcc将被看作是一个名字,因为它们的前8个字符相同。
可供使用的字符有小写字母,大写字母,数字和下划线。第一个字符必须是字母或者下划线。C语言的名字是区分大小写的,即把一个大写字母和与之对应的小写字母看作是不同的。因此,clang不同于Clang或者CLANG。
num = 1; // =(赋值运算符)
这行程序是一个赋值语句(assignment statement)。赋值语句是C语言的基本操作之一。这个特殊的例子的意思是“把值1赋给变量num”。前面的int num;语句在计算机内存中为变量num分配了空间,该赋值语句在那个地方为变量存储了一个值。如果你想的话,以后你还可以给num赋另一个值,这就是把num称为变量的原因。可以把变量理解成是一个容器,用来盛放常量的。另外注意赋值语句赋值的顺序是从右到左。同样,该语句也用分号结束。
printf("Hello!");
printf("I am dotcpp.com\n");
printf("My birthday is %d\n", num);
所有这些行都使用了C语言的一个标准函数:printf()。圆括号表明printf是一个函数名。圆括号中包含的内容是从函数main()传递到函数printf()的信息。例如,第一行把Hello!传递给printf()函数。 这样的信息被称为参数(argument),更完整的名称是函数的实际参数(actual argument)。printf()函数如何处理这个参数?程序将识别两个双引号之间的内容并把它们显示在屏幕上。
第一行printf()语句是如何在C语言中调用(call)或请求(invoke)一个函数的例子。只须键入函数的名字,把所需的一个或多个参数放进圆括号中。当程序运行到这一行时,控制权将转给该函数(在这个例子中是 printf())。当函数完成了它所要做的工作,将控制权返回给原来的函数(调用函数),在这个例子中是main()。
那么下一个printf()行呢?引号中有字符\n,但并没有输出它们!发生了什么事情呢?\n字符的意思是开始新的一行。\n组合(依次键入这两个字符)代表一个称为**换行符(newline character)**的字符,它意味着“在下一行的最左边开始新的一行”。换句话说,打印换行字符的效果和在普通键盘上按下回车键一样。当键入printf()这个参数时,为什么不直接按回车键呢?因为那将看作是直接针对编辑器的命令,而不是作为存在源代码中的指令。也就是说,当你按回车键时,编辑器退出你正在输入的当前行,并开始新的一行。而换行符则影响程序的输出如何显示。
换行符是**转义字符(Escape Sequence)**的一个例子。转义字符通常用于代表难于表达的或是无法键入的字符。其他的例子比如\t代表Tab键,\b代表退格键盘。每个转义字符都用斜线字符(\)开始。
这样就解释了三个printf()语句只产生两行输出的原因:第一个printf指令中没有换行字符,而第二个和第三个都有。最后一个printf()行中又有一个奇怪的问题:当输出这一行时%d起什么作用?回忆一下,这一行的输出结果是:
My birthday is 2014
喔!当这一行输出时,数字2014被符号组合%d代替了,而1是变量 num的值。%d是一个占位符,其作用是指出输出num值的位置。
事实上,%告诉程序把一个变量在这个位置输出,d告诉程序将输出一个十进制(以10为基数)整数变量。printf()函数允许多种输出变量格式,包括十六进制(以16为基数)整数和带小数点的数。实际上,printf()中的f暗示着这是一种格式化(format)的输出函数。每一种数据都有自己的说明符,往后会有更多的说明。
return 0;
return语句(返回语句)是程序的最后一个语句。在int main(void)中int表示main()函数的返回值应该是一个整数。C标准要求main()这样做。带有返回值的C语言函数要使用一个return语句,该语句包括关键字return,后面紧跟着要返回的值,然后是一个分号。对于main()函数来说,如果你漏掉了return语句,则大多数编译器将对你的疏忽提出警告,但仍将编译该程序。此时,你可以暂时把main()中的return语句看作是保持逻辑连贯性所需的内容。但对于某些操作系统(包括DOS和UNIX)而言,它有实际的用途。
C 程序由各种令牌组成,令牌可以是关键字、标识符、常量、字符串值,或者是一个符号。例如,下面的 C 语句包括五个令牌
在 C 程序中,分号是语句结束符。也就是说,每个语句必须以分号结束。它表明一个逻辑实体的结束。
printf("Hello world!!! \n") ;
// 注释1 ---单行注释
/*
注释2 -- 多行注释
*/
/*注释3 --- 单、多行注释都可以*/
不能在注释内嵌套注释,注释也不能出现在字符串或字符值中。
在程序中使用的变量名、函数名、标号等统称为标识符。除库函数的函数名由系统定义外,其余都由用户自定义。
标识符:是有程序员定义的单词,用来给程序中的数据、函数和其他用户自定义对象的命名。
C规定,标识符只能是字母(A~Z,a~z)、数字(0~9)、下划线(_)组成的字符串,并且其第一个字符必须是字母或下划线。且不能与C语言的关键字重名(关键字见下一节)。
此外,在使用标识符时还必须注意以下几点:
(1)标准C不限制标识符的长度,但它受各种版本的C语言编译系统限制,同时也受到具体机器的限制。例如在某版本C中规定标识符前八位有效,当两个标识符前八位相同时,则被认为是同一个标识符。
(2)在标识符中,大小写是有区别的。例如CLANG和Clang 是两个不同的标识符。
(3)标识符虽然可由程序员随意定义,但标识符是用于标识某个量的符号。因此,命名应尽量有相应的意义,以便阅读理解,做到“顾名思义”。
程序设计语言本身会定义一些专用单词,称之为保留字或关键字,它们具有特定的含义,程序员不能另作他用。
如C语言规定了32个关键字
auto | double | int | struct |
---|---|---|---|
break | else | long | switch |
case | enum | register | typedef |
char | extern | return | union |
const | float | short | unsigned |
continue | for | signed | void |
default | goto | sizeof | volatile |
do | if | static | while |
auto | 声明自动变量 |
---|---|
break | 跳出当前循环 |
case | 开关语句分支 |
char | 声明字符型变量或函数返回值类型 |
const | 定义常量,如果一个变量被 const 修饰,那么它的值就不能再被改变 |
continue | 结束当前循环,开始下一轮循环 |
default | 开关语句中的"其它"分支 |
do | 循环语句的循环体 |
double | 声明双精度浮点型变量或函数返回值类型 |
else | 条件语句否定分支(与 if 连用) |
enum | 声明枚举类型 |
extern | 声明变量或函数是在其它文件或本文件的其他位置定义 |
float | 声明浮点型变量或函数返回值类型 |
for | 一种循环语句 |
goto | 无条件跳转语句 |
if | 条件语句 |
int | 声明整型变量或函数 |
long | 声明长整型变量或函数返回值类型 |
register | 声明寄存器变量 |
return | 子程序返回语句(可以带参数,也可不带参数) |
short | 声明短整型变量或函数 |
signed | 声明有符号类型变量或函数 |
sizeof | 计算数据类型或变量长度(即所占字节数) |
static | 声明静态变量 |
struct | 声明结构体类型 |
switch | 用于开关语句 |
typedef | 用以给数据类型取别名 |
unsigned | 声明无符号类型变量或函数 |
union | 声明共用体类型 |
void | 声明函数无返回值或无参数,声明无类型指针 |
volatile | 说明变量在程序执行中可被隐含地改变 |
while | 循环语句的循环条件 |
包含空格的行,被称为空白行,可能带有注释,C 编译器会完全忽略它。
在 C 中,空格用于描述空白符、制表符、换行符和注释。空格分隔语句在这里插入代码片
的各个部分,让编译器能识别语句中的某个元素(比如 int)在哪里结束,下一个元素在哪里开始
int age ;
/*这里,int 和 age 之间必须至少有一个空格字符(通常是一个空白符),这样编译器才能够区分它们.*/