[华为云在线课程][C语言基础][一][入门][学习笔记]
[华为云在线课程][C语言基础][一][入门][学习笔记]
1.入门
1.1.Helloworld
C语言的"hello, world"
/*
* Windows平台运行C语言,编写.c文件,控制台中输入gcc 文件名.c,最后得出一个exe可执行程序。
* */
//包含标准库的信息
#include <stdio.h>
//定义名为main的函数,这函数不接受参数值
int main()
{
//花括号里的是main函数的语句
//main函数调用库函数printf显示字符序列
//\n符号表示换行符
printf("hello, world!\n");
}
一个C语言程序,无论大小都是由函数和变量组成的。函数中含有一些语句指定所要执行的计算操作;变量则用于存储计算过程中使用的值。通常函数的命名没有限制,但main是一个特殊的函数名-每个程序都从main函数的起点开始执行,这意味着每个程序都必须在某个位置包含一个main函数。main函数会调用其他函数完成工作,helloworld例子中的#include<stdio.h>
告诉编译器包含标准输入/输出库的信息。
函数之间进行数据交换的一种方法是调用函数向被调用函数提供一个值(称为参数)列表。函数名后面的一对圆括号将参数列表括起来。不需要参数就用空参数表()表示。
用双括号括起来的字符序列称为字符串或字符串常量,\n
表示换行符,printf函数不会自动换行。
练习1-1:
/*
* 在helloworld程序中去掉部分内容,观察出错信息。
* */
#include <stdio.h>
int main(){
//去除了printf函数后面的分号后提示"Expected ';' after expression"
printf("hello, world!\n")
return 0;
}
练习1-2:
/*
* 当printf函数的参数字符串中包含\c时,观察出现什么情况
* */
#include <stdio.h>
int main(){
//提示:"Unknown escape sequence '\c'"
printf("hello,world!\c");
return 0;
}
2.变量与算术表达式
使用公式℃=(5/9)(℉-32)
打印一个华氏度与摄氏度温度对照表。
#include <stdio.h>
/*
* 当 fahr=0,20,..,300时,分别打印华氏温度与摄氏温度对照表
* */
int main() {
float fahr, celsius;
float lower, upper, step;
//温度表下限
lower = 0;
//温度表上限
upper = 300;
//步长
step = 20;
fahr = lower;
while (fahr <= upper) {
celsius = (5.0 / 9.0) * (fahr - 32.0);
printf("%3.0f %6.1f\n", fahr, celsius);
fahr += step;
}
}
在C语言中,所有变量都必须先声明后使用。声明通常放在函数起始处,在任何可执行语句之前。声明用于说明变量的属性,由一个类型名和一个变量表组成。
其中,int类型表示整数,float类型表示浮点数。char类型表示字符(一个字节),short类型表示短整型,long类型表示长整型,double类型表示双精度浮点型。
在上面的程序中,完成了变量声明后,就把下限值lower赋给华氏度变量,然后开始执行while循环输出华氏度转摄氏度的结果。每次温度值自增20并输出对应结果。
关于输出格式问题
表达式 | 含义 |
---|---|
%d | 按照十进制整型数打印 |
%6d | 按照十进制整型数打印,至少6个字符宽 |
%f | 按照浮点数打印 |
%6f | 按照浮点数打印,至少6个字符宽 |
%.2f | 按照浮点数打印,小数点后有两位小数 |
%6.2f | 按照浮点数打印,至少6个字符宽,小数点后有两位小数。 |
%o | 八进制 |
%x | 十六进制 |
%c | 字符 |
%s | 字符串 |
%% | 百分号本身 |
练习1-3:
/*
* 修改温度转换程序,使之能在转换表的顶部打印一个标题。
* */
#include <stdio.h>
int main() {
float fahr, celsius;
float lower, upper, step;
//温度表下限
lower = 0;
//温度表上限
upper = 300;
//步长
step = 20;
//将下限值赋值给fahr
fahr = lower;
//打印表头
printf(" 华氏度转摄氏度表\n");
while (fahr <= upper) {
celsius = (5.0 / 9.0) * (fahr - 32.0);
printf("%3.0f %18.1f\n", fahr, celsius);
fahr += step;
}
}
练习1-4:
/*
* 打印摄氏度转华氏度的转换表
* */
#include <stdio.h>
int main() {
float fahr, celsius;
float lower, upper, step;
//温度表下限
lower = 0;
//温度表上限
upper = 300;
//步长
step = 20;
celsius = lower;
printf(" 摄氏度转华氏度表\n");
while (celsius <= upper) {
fahr = (9.0 / 5.0) * celsius + 32.0;
printf("%3.0f %18.1f\n", celsius, fahr);
celsius += step;
}
}
3.for语句
对于上面的转换表,也可以使用for循环来实现:
//打印华氏度-摄氏度转换表
#include<stdio.h>
int main() {
int fahr;
for (fahr = 0; fahr <= 300; fahr += 20) {
printf("%3d %6.1f\n", fahr, (5.0 / 9.0) * (fahr - 32));
}
}
在for语句中,温度下限、上限和步长都是常量,计算摄氏温度的表达式变成printf函数的第三个参数。for语句包括3个部分,各部分之间用分号隔开。最左边的是初始化部分,仅在进入循环前执行一次。中间的是控制循环的测试或条件部分。循环控制将对该条件求值,如果结果值为真则执行循环体。最右边以特定步长每次增加,并再次对条件求值。如果得到条件值为假则终止执行。
在工作过程中,选择选择while与for中的任意一种循环语句,主要看使用哪一种更清晰。for语句比较适合初始化和增加步长都是单条语句并且逻辑相关的情形,因为它将循环控制语句集中在一起,比while语句更紧凑。
练习1-5:
/*
* 逆序输出(按照从300度到0度的顺序)打印温度转换表
* */
#include <stdio.h>
int main() {
float fahr, celsius;
int lower, upper, step;
lower = 0;
upper = 300;
step = 20;
for (celsius = upper; celsius >= lower; celsius -= step) {
fahr = (9.0 / 5.0) * celsius + 32.0;
printf("%3.0f %6.1f\n", celsius, fahr);
}
return 0;
}
4.符号常量
#define
指令可以把符号名(符号常量)定义为一个特定的字符串:#define 名字 替换文本
在该定义之后,程序中出现的所有在#define
中定义的名字都将用相应的替换文本替换。其中,名字与普通变量名的形式相同:都是以字母打头的字母和数字序列;替换文本可以是任何字符序列,而不仅限于数字。
#include <stdio.h>
#define LOWER 0
#define UPPER 300
#define STEP 20
int main() {
int fahr;
for (int fahr = LOWER; fahr <= UPPER; fahr += STEP) {
printf("%3d %6.1f\n", fahr, (5.0 / 9.0) * (fahr - 32));
}
}
其实,LOWER、UPPER与STEP都是符号常量,而非变量,因此不需要出现在声明中。符号常量名通常用大写字母拼写,这样容易与用小写字母拼写的变量名区别。注意,指令行末没有分号。
5.字符输入/输出
标准库提供的输入/输出模型非常简单。其输入/输出都是按照字符流的方式处理。文本流是由多行字符构成的字符序列,每行字符则由0个或多个字符组成,行末是一个换行符。
标准库提供了一次读/写一个字符的函数,其中最简单的是getchar和putchar两个函数。每次调用时,getchar函数从文本流中读入下一个输入字符,并将其作为结果值返回。在执行c=getchar()
后,变量c包含输入流下一个字符。字符通常通过键盘输入。每次调用putchar函数时将打印一个字符。
5.1.文件复制
最简单的例子就是把输入一次一个字符地复制到输出:
#include <stdio.h>
int main() {
int c;
c = getchar();
while (c != EOF) {
putchar(c);
c = getchar();
}
}
字符在键盘、屏幕或其他的任何地方无论以什么形式表现,在机器内部都是以位模式存储的。char类型专门用于存储这种字符型数据,当然任何整型(int)也可以用于存储字符型数据。
如何区分有效数据与输入结束符的问题。C语言采取的解决方法是:在没有输入时,getchar函数将返回一个特殊值,称为EOF(end of file,文件结束)。EOF定义在头文件<stdio.h>
中,是个整型数。
练习1-6:
/*
* 验证表达式getchar()!=EOF的值是0还是1
* */
#include<stdio.h>
int main(){
printf("请按下回车键\n\n");
printf("表达式getchar()!=EOF值为: %d\n",getchar()!=EOF);
//表达式getchar()!=EOF值为: 1
return 0;
}
练习1-7:
/*
* 编写一个打印EOF值的程序
* */
#include <stdio.h>
int main(){
printf("EOF的值为:%d\n", EOF);
//EOF的值为:-1
return 0;
}
5.2.字符计数
#include <stdio.h>
//1st version
int main() {
long nc;
nc = 0;
while (getchar() != EOF) {
++nc;
}
printf("%ld\n", nc);
}
其中,语句++nc等价于nc=nc+1。输出类型表示长整型输出,使用double类型可以处理更大的数字。也可以使用for循环实现:
#include <stdio.h>
//2nd version
int main(){
double nc;
for (nc = 0; getchar() != EOF; ++nc)
;
printf("%.0f\n", nc);
}
printf函数都使用%f进行说明。%.0f强制不打印小数点和小数部分,因此小数部分的位数为0。这段程序中for语句的循环体是空的,因为所有工作都在测试(条件)部分与增加步长部分完成了。C语言的语法规则要求for循环语句必须有一个循环体,因此用单独的分号代替。
5.3.行计数
标准库保证输入文本流以行序列的形式出现,每一行均以换行符结束。因此,统计行数等价于统计换行符的个数。
#include <stdio.h>
int main() {
int c, nl;
nl = 0;
while ((c = getchar()) != EOF) {
if (c == '\n') {
++nl;
}
}
printf("%d\n", nl);
}
练习1-8:
/*
* 编写一个统计空格、制表符与换行符个数的程序
* */
#include <stdio.h>
int main()
{
int blanks, tabs, newlines;
int c;
int done = 0;
int lastchar = 0;
blanks = 0;
tabs = 0;
newlines = 0;
while (done == 0)
{
c = getchar();
if (c == ' ')
{
++blanks;
}
if (c == '\t')
{
++tabs;
}
if (c == '\n')
{
++newlines;
}
if (c == EOF)
{
if (lastchar != '\n')
{
++newlines;
}
done = 1;
}
lastchar = c;
}
printf("\n空格:%d\n制表符:%d\n换行符:%d\n", blanks, tabs, newlines);
return 0;
}
练习1-9:
/**
* 编写一个将输入复制到输出的程序,并将其中连续的多个空格用一个空格代替。
*/
#include <stdio.h>
int main()
{
int c;
int space;
space = 0;
while ((c = getchar()) != EOF)
{
if (c == ' ')
{
if (space == 0)
{
space = 1;
putchar(c);
}
}
if (c != ' ')
{
space = 0;
putchar(c);
}
}
return 0;
}
练习1-10:
/**
* 编写一个将输入复制到输出的程序,并将其中的制表符替换为\t,
* 把回退符替换为\b,把反斜杠替换为\\。
*/
#include <stdio.h>
int main()
{
//字符
int c;
//符号表示
int flag;
while ((c = getchar()) != EOF)
{
flag = 0;
if (c == '\t')
{
putchar('\\');
putchar('t');
flag = 1;
}
if (c == '\b')
{
putchar('\\');
putchar('b');
flag = 1;
}
if (c == '\\')
{
putchar('\\');
putchar('\\');
flag = 1;
}
if (flag == 0)
{
putchar(c);
}
}
return 0;
}
5.4.单词计数
下面这段程序是UNIX系统中wc程序的骨干部分:
#include <stdio.h>
#define IN 1
#define OUT 0
/* count lines, words, and characters in input */
main()
{
int c, nl, nw, nc, state;
state = OUT;
nl = nw = nc = 0;
while ((c = getchar()) != EOF)
{
++nc;
if (c == '\n')
{
++nl;
}
if (c == ' ' || c == '\n' || c == '\t')
{
state = OUT;
}
else if (state == OUT)
{
state = IN;
++nw;
}
}
printf("%d %d %d\n", nl, nw, nc);
}
程序执行时,遇到单词的第一个字符,就作为新单词加以统计。state变量记录程序当前是否正位于一个单词之中,初值是不在单词中,赋值为OUT。
nl=nw=nc=0;
,在兼有值与赋值两种功能的表达式中,赋值结合次序是由右至左。这条语句等价于nl=(nw=(nc=0));
。
练习1-12:
/**
* 编写一个程序,以每行一个单词的形式打印其输入
*/
#include <stdio.h>
int main()
{
int c;
int space;
space = 0;
while ((c = getchar()) != EOF)
{
if (c == ' ' || c == '\t' || c == '\n')
{
if (space == 0)
{
space = 1;
putchar('\n');
}
}
else
{
space = 0;
putchar(c);
}
}
return 0;
}
6.函数
函数为计算的封装提供了一种简便的方法,此后使用函数时不需要考虑它是如何实现的。使用正确设计的函数,程序员无需考虑功能是如何实现的,而只需知道函数具有哪些功能就够了。
函数定义的一般形式为:
返回值类型 函数名(0个或多个参数声明)
{
声明部分
语句序列
}
函数定义可以任意出现在一个源文件或者多个源文件中,但同一函数不能分割存放在多个文件中。如果源程序分散在多个文件中,那么在编译和加载时就需要做更多的工作。以下是一个函数power(m,n)的定义及调用它的主程序。
#include <stdio.h>
int power(int m, int n);
int main() {
for (int i = 0; i < 10; ++i) {
printf("%d %d %d\n", i, power(2, i), power(-3, i));
}
return 0;
}
int power(int base, int n) {
int p;
p = 1;
for (int i = 0; i <= n; ++i) {
p = p * base;
}
return p;
}
power函数的第一行语句int power(int base, int n)
表示声明参数的类型、名字以及该函数返回结果的类型。power函数的参数使用的名字只在power函数内部有效,对其他函数是不可见的:其他函数可以使用相同的参数名字不会冲突。这个声明称为函数原型,必须与power函数定义和用法一致。
通常把函数定义中圆括号内列表中出现的变量成为形式参数,而把函数调用中与形式参数对应的值称为实际参数。
练习1-15:
/*
* 重新编写温度转换程序,使用函数实现温度转换计算
* */
#include <stdio.h>
float convert(float a);
int main() {
float fahr, celsius;
int lower, upper, step;
lower = 0;
upper = 300;
step = 20;
printf("华氏度 摄氏度\n");
fahr = lower;
while (fahr <= upper) {
celsius = convert(fahr);
printf("%3.0f %6.1f\n", fahr, celsius);
fahr += step;
}
}
float convert(float a) {
float c;
c = (5.0 / 9.0) * (a - 32.0);
return c;
}
7.参数-传值调用
在C语言中,所有函数都是"通过值"传递的,就是说传递给被调用函数的参数值存放在临时变量中,而不是存放在原来的变量中。
在C语言中,被调用函数不能直接修改主调函数中变量的值,而只能修改其私有的临时副本的值。传值调用的利大于弊。在被调用函数中,参数可以看作是便于初始化的局部变量,因此额外使用的变量更少。程序可以更加紧凑。
8.字符数组
字符数组是C语言最常用的数组类型。通过一个程序来说明字符数组及操作字符数组的函数的用法。该程序读入一组文本行,并把最长的文本行打印出来。算法框架如下:
while (还有未处理得行)
if (该行比已处理的最长行更长)
保存该行为最长行
保存该行的长度
打印最长的行
/**
* 读入行,保存最长行并进行输出。
*/
#include <stdio.h>
#define MAXLINE 50 /* 最大输入行长 */
int getline(char line[], int maxline);
void copy(char to[], char from[]);
int main()
{
int len; /* 当前行长度 */
int max; /* 最长行长 */
char line[MAXLINE]; /* 保存当前输入行 */
char longest[MAXLINE]; /* 保存最长行长度 */
max = 0;
while ((len = getline(line, MAXLINE)) > 0)
{
if (len > max)
{
max = len;
copy(longest, line);
}
}
if (max > 0)
{
printf("%s", longest);
}
return 0;
}
/* 读入行到s数组,返回长度 */
int getline(char s[], int lim)
{
int c, i;
for (i = 0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; ++i)
{
s[i] = c;
}
if (c == '\n')
{
s[i] = c;
++i;
}
s[i] = '\0';
return i;
}
/* 把from复制到to */
void copy(char to[], char from[])
{
int i;
i = 0;
while ((to[i] = from[i]) != '\0')
{
++i;
}
}
main与getline之间通过一对参数及一个返回值进行数据交换。其中getline函数把字符'\0'
(即空字符,值为0)放到数组末尾以标记字符串的结束。
练习1-16:
/*
* 修改打印最长文本行的程序的主程序main,使之可以打印任意长度的输入行的长度,尽可能多地打印文本
* */
#include <stdio.h>
#define MAXLINE 20 /* 最大输入行长 */
int getline(char s[], int lim);
void copy(char to[], char from[]);
int main() {
char line[MAXLINE];
char longest[MAXLINE];
char temp[MAXLINE];
int len, max, prevmax, getmore;
max = prevmax = getmore = 0;
while ((len = getline(line, MAXLINE)) > 0) {
if (line[len - 1] != '\n') {
if (getmore == 0) {
copy(temp, line);
}
prevmax += len;
if (max < prevmax) {
max = prevmax;
}
getmore = 1;
} else {
if (getmore == 1) {
if (max < prevmax + len) {
max = prevmax + len;
copy(longest, temp);
longest[MAXLINE - 2] = '\n';
}
getmore = 0;
} else if (max < len) {
max = len;
copy(longest, line);
}
prevmax = 0;
}
if (max > 0) {
printf("%s", longest);
printf("len = %d\n", max);
}
return 0;
}
}
/* 读入行到s数组,返回长度 */
int getline(char s[], int lim) {
int c, i;
for (i = 0;
i < lim - 1 && (c = getchar()) != EOF && c != '\n';
++i) {
s[i] = c;
}
if (c == '\n') {
s[i] = c;
++i;
} else if (c == EOF && i > 0) {
s[i] = '\n';
++i;
}
s[i] = '\0';
return i;
}
void copy(char to[], char from[]) {
int i;
i = 0;
while ((to[i] = from[i]) != '\0') {
++i;
}
}
练习1-17:
/*
* 编写一个程序,打印长度大于10个字符的所有输入行
* */
#include <stdio.h>
#define MINLENGTH 11
int readbuff(char *buffer) {
size_t i = 0;
int c;
while (i < MINLENGTH) {
c = getchar();
if (c == EOF) return -1;
if (c == '\n') return 0;
buffer[i++] = c;
}
return 1;
}
int copyline(char *buffer) {
size_t i;
int c;
int status = 1;
for (i = 0; i < MINLENGTH; ++i) {
putchar(buffer[i]);
}
while (status == 1) {
c = getchar();
if (c == EOF) {
status = -1;
} else if (c == '\n') {
status = 0;
} else {
putchar(c);
}
}
putchar('\n');
return status;
}
int main() {
char buffer[MINLENGTH];
int status=0;
while (status != -1) {
status = readbuff(buffer);
if (status == 1) {
status = copyline(buffer);
}
}
return 0;
}
9.外部变量与作用域
上面例子中的main函数中的变量是main函数的私自变量或局部变量。由于他们都是main函数中声明的,因此其他函数不能直接访问它们。其他函数中声明的变量也同样如此。函数中的每个局部变量只在函数被调用时存在,在函数执行完毕退出时消失。这也是其他语言通常把这类变量称为自动变量的原因。
除了自动变量外,还可以定义位于所有函数外部的变量,外部变量在程序执行期间一直存在,而不是在函数调用时产生、在函数执行完毕时消失。
外部变量必须定义在所有函数之外,且只能定义一次,定义后编译程序将为它分配存储单元。在每个需要访问外部变量的函数中,必须声明相应的外部变量并说明类型。声明时可以用extern语句显式声明,也可以通过上下文隐式声明。某些情况下可以省略extern声明。在源文件中,如果外部变量的定义出现在使用它的函数之前,那么在这个函数中就没有必要使用extern声明。
如果程序包含在多个源文件中,而某个变量在file1文件中定义、在file2和file3文件中使用那么在文件file2与file3中就需要使用extern声明来建立该变量与其定义之间的联系。通常把变量和函数的extern声明放在一个单独的文件中(头文件),并在每个源文件的开头使用#include语句把所要用的头文件包含进来。
- 点赞
- 收藏
- 关注作者
评论(0)