第一章 导言
1.8 参数——传值调用
核心观点:被调用函数不能直接修改主调用函数中变量的值,除非通过变量地址传参
#include <stdio.h>
void add(int a, int b) {
a = a + b;
}
void sub(int *a, int b) {
(*a) = (*a) - b;
}
int main() {
int a = 2;
int b = 1;
/* 只传递值 */
printf("a = %d, b = %d\n",a,b);
add(a,b);
printf("a = %d, b = %d\n",a,b);
/* 传递该值对应的地址 */
sub(&a, b);
printf("a = %d, b = %d\n", a,b);
return 0;
}
运行结果:
- 第一次printf:打印a和b的初值
- 第二次printf:main函数通过传值调用完add函数后,打印a和b的值,add函数中改变了a的值,但不改变main函数中a的值
- 第三次printf:main函数通过传地址调用完sub函数后,打印a和b的值,sub函数中改变了a的值,同时改变了main函数中a的值
1.9 字符数组
核心代码
读入一组文本行,并把最长的文本行打印出来;其中读入文本行getline和拷贝最长文本行copy动作都需要自己实现
备注:由于stdio系统库里面已经实现了getline函数,所以此处将该函数命名为getline_bak
#include <stdio.h>
#define MAXLINE 100
int getline_bak(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_bak(line, MAXLINE)) > 0) {
if (len > max) {
copy(longest, line);
max = len;
}
}
if (max > 0) {
printf("%s", longest);
}
return 0;
}
/* 功能:获取文本行,识别到换行符、文件终止符、文本字符数量超过最大值,则停止记录
* 返回值:返回文本行的字符串个数
*/
int getline_bak(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;
}
void copy(char to[], char from[])
{
int i = 0;
while ((to[i] = from[i]) != '\0')
i++;
}
运行结果
练习1-16
修改打印最长文本行的程序的主程序,使之可以打印任意长度的输入行的长度,并且尽可能多的打印文本(即打印所有长度的输入行)
思路:只需要在getline_bak之后统一打印len和line即可
练习1-17
编写一个程序,打印长度大于80个字符的所有输入行
思路:getline之后判断len是否大于80,大于80就打印(这里便于演示运行结果将长度下限改为10,即文本行的长度如果大于10就打印)
练习1-18
编写一个程序,删除每个输入行末尾的空格及制表符,并删除完全是空格的行
思路:在getlinebak函数中判断获取到的字符是否为空格或制表符,如果是就不记录
练习1-19
编写函数reverse(s),将字符串s中的字符顺序颠倒过来,使用该函数编写一个程序,每次颠倒一个输入行中的字符顺序
思路:使用两个数组下标,分别指向数组的开头和结尾,将开头和结尾的字符进行对调,一直到开头的下标大于结尾的下标为止
#include <stdio.h>
#define MAXLINE 100
int getline_bak(char line[], int maxline);
void copy(char to[], char from[]);
void reverse(char s[]);
int main()
{
int len;
int max;
char line[MAXLINE];
char longest[MAXLINE];
max = 0;
while ((len = getline_bak(line, MAXLINE)) > 0) {
reverse(line);
printf("len = %d, line = %s\n", len, line);
if (len > max) {
copy(longest, line);
max = len;
}
}
if (max > 0) {
printf("%s", longest);
}
return 0;
}
/* 功能:获取文本行,识别到换行符、文件终止符、文本字符数量超过最大值,则停止记录
* 返回值:返回文本行的字符串个数
*/
int getline_bak(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;
}
void copy(char to[], char from[])
{
int i = 0;
while ((to[i] = from[i]) != '\0')
i++;
}
void reverse(char s[])
{
int i, j;
char tmp;
while (s[i] != '\0') {
i++;
}
--i;
if (s[i] == '\n') {
--i;
}
j = 0;
while (j < i) {
tmp = s[j];
s[j] = s[i];
s[i] = tmp;
j++;
i--;
}
}
运行结果
1.10外部变量与作用域
- 定义:创建变量或分配内存单元
- 声明:说明变量的性质,但不分配内存单元
内部变量:在函数内部定义的变量
外部变量:在函数外部定义的变量,函数使用该变量前需要使用extern对该变量进行声明
练习1-20
编写detab程序,将输入中的制表符替换成适当数目的空格,使空格充满到下一个制表符终止位的地方,假设制表符终止的位置是固定的,比如每隔n列就会出现一个制表符终止位。n应该作为变量还是符号常量呢?
思路:由于每隔n列就会出现一个制表符终止位,只要识别到制表位就根据终止位的列数来打印空格(由于空格无法看到效果,此处用“_”替代)
#include <stdio.h>
#define TABING 8 //假设制表符终止位每隔8列出现一次
int main()
{
int c, nb, pos;
nb = 0;
pos = 1;
while ((c = getchar()) != EOF) {
if (c == '\t') {
nb = TABING - (pos - 1) % TABING;
while (nb > 0) {
putchar('_');
pos++;
nb--;
}
} else if (c == '\n') {
putchar(c);
pos = 1;
} else {
putchar(c);
pos++;
}
}
return 0;
}
运行结果
练习1-21
编写程序entab,将空格串替换为最少数量的制表符和空格,但要保持单词之间的间隔不变,假设制表符终止位的位置与练习1-20的detab程序的情况相同,当使用一个制表符或者空格都可以达到下一个制表符终止位时,选用哪一种替换字符比较好
思路:在字符串长度是制表符终止位长度的倍数时,则使用制表符替代,否则使用空格进行替代(替代前空格使用*,替代后空格使用_表示)
#include <stdio.h>
#define TABING 8
int main()
{
int c, nb, nt, pos;
nb = 0;
nt = 0;
for (pos = 0; (c = getchar()) != EOF; pos++) {
if (c == '*') {
if (pos % TABING != 0) {
nb++;
} else {
nb = 0;
nt++;
}
} else {
for (;nt > 0; nt--) {
putchar('\t');
}
if (c == '\t') {
nb = 0;
} else {
while (nb > 0) {
putchar('_');
nb--;
}
}
putchar(c);
if (c == '\n') {
pos = 0;
}
if (c == '\t') {
pos = pos + (TABING - (pos - 1) % TABING) - 1;
}
}
}
return 0;
}
运行结果