coursera《计算机程式设计》学习笔记6

week6

台湾大学 刘邦锋老师讲授


1. 字符 character

定义字符变量,字符在C语言中是 char,C 语言中使用一个字节的长度(8位)来存储字符,所以一个字符能存一个 -128 到 127 之间的整数


范例2:(char-size.c)一个 char 所占的字节数

#include<stdio.h>
int main(void)
{
    char c;
    printf(" %d\n ", sizeof(c));  //答案是1,即占1个字节的长度
    return 0;
}

片语3:字符的输出

char c;    // <span style="color:#ff0000;">也可以声明成 int c; 因为int 本身包括 4 个字节,存放字符有充足的位置,但不要这样做</span><pre name="code" class="objc">c = 97;  // 对应小写字母 a <pre name="code" class="objc">printf(" %c ", c);
 
 

ASCII 码,就是将 0 到 127 的整数对应到我们常用的英文大小写字母,0 到 9 的数字,以及标点符号等。

%c 中的 c 代表字符。


2. 字符常数

C 语言中字符常数使用一对单引号将一个符号括起来,代表它的ASCII 值。这样就不需要时刻牢记字符和与之相对应的 ASCII 表中的数字了~

#include<stdio.h>
int main(void)
{
    char c;
    c = 'm';
    printf(" %c ", c);
    c = ' \n ';
    printf(" %c ", c);
    return 0;
}

3. 学习要点

除了表示的范围较小以外,char 的用法与 int 并无太大差别

#include<stdio.h>
int main(void)
{
    char c;
    int i;
    scanf("%d", &i );    //输入 i 为 127
    c = i;
    c++;
    i++;
    printf("c = %d\n", c );
    printf("i = %d\n", i );
    return 0;
}

结果输出 c = -128,i = 128。 char 能表示的范围很小,所以很容易溢位overflow


4. 字符的输入

scanf("%c", &c);

范例10:(char-io.c)输入一字符再分别用字符或整数输出

#include<stdio.h>
int main(void)
{
    char c;
    scanf("%c", &c);
    printf("%c\n", c);
    printf("%d\n", c);
    return 0;
}

输入:a

输出:a

          97


片语11:借由 scanf 的返回值判定是否还有数据未读入

while (scanf("%c", &c) != EOF)
{
    ...
    process character c;  //在循环中对读入的 c 一个接一个进行处理<pre name="code" class="objc">}
 

在编译环境中程序会自己独到 EOF ,可是在命令行环境的话,当你出入“ab回车”的时候,程序会分别对a、b、回车进行处理,然后不会自动停止,光标会闪闪闪,等待新的输入(ctrl + d 强制停止执行),要怎样才能让程序知道自己读到数据结尾了呢?

在ubuntu linux 系统下直接输入 ctrl + d 就可以了。windows 输入 ctrl + c 即可。


片语12:使用字符分类函数

#include<ctype.h>
...
char c;
...
if (isxxxxx(c))

系统的 <ctype.h> 中定义了一些函数:字符分类函数、字符转换函数

字符分类函数:

isalnum()     如果参数为字母数字,则返回ture

isalpha()      如果参数为字母,则返回ture

islower()      如果参数是小写字母,则返回ture

isupper()     如果参数为大写字母,则返回ture

isdigit()        如果参数为数字(0-9),则返回ture

isxdigit()      如果参数为十六进制数,则返回ture

isprint()        如果参数为打印字符,包括空格,则返回ture

isgraph()      如果参数为空格之外的打印字符,则返回ture

isspace()     如果参数为标准空白字符(空格,进纸,换行符,回车,水平制表符,垂直制表符),则返回ture

ispunct()      如果参数为标点符号,则返回ture

iscntrl()        如果参数为控制字符,则返回ture


范例14:(ascii-dec.c)用十进制来打印ACSII 表

#include<stdio.h>
int main(void)
{
    int c;
    printf("   0123456789\n"); //前面空了3格
    for(c = 30; c <= 127; c++)
    {
        if(c % 10 == 0)
            printf("%2d ", c / 10); //每行开头打印行标,而且空一格
        if(isprint(c))
            printf("%c", c);
        else
            printf(" ");//如果不可打印,就印空格
        if(c % 10 == 9)
            printf("\n");//如果是每行末尾,则印换行
    }
    return 0;
}

输出如下:


字符转换函数

tolower()     如果参数为大写字母,即转化为小写字母,否则返回原值
toupper()     如果参数为小写字母,即转化为大写字母,否则返回原值

5. 字符串

片语1:定义字符串

char s[80];
前面的 s 就是一个字符,长度为80个字符的一个字符串,共80 个字节

#include<stdio.h>
int main(void)
{
    char s[80];
    printf("%d\n", sizeof(s));
    return 0;
}
输出:80


范例3:(string-init.c)使用数组的方式初始化一个字符串

#include<stdio.h>
int main(void)
{
    char s[80] = {'m', 'a', 'i', 'n', '(', ')', '\n', '{', '\n', '}', '\n'};  //后面没赋值的地方会自动补 0 
    int i;
    for (i = 0; i < 11; i ++)
        printf("%c", s[ i ]);
    return 0;
}
字符串处理惯例:字符串范围到 '\0' 这个特殊字符为止。这个特殊字符的1个字节 8 位 中所有元素都为 0。

片语4:以 printf 打印出字符串

printf("%s", string);  // 注意是 %s

所以,在范例3 中的程序中 ,可以改写成:

<pre name="code" class="objc">#include<stdio.h>
int main(void)
{
    char s[80] = {'m', 'a', 'i', 'n', '(', ')', '\n', '{', '\n', '}', '\n','\0'};  //在字符串后面自己手动加 一个 '\0',使打印的时候遇到它就停止
    int i;
    printf("%s", s)   // 不需要知道字符串长度<pre name="code" class="objc">    return 0;
}
 
 
 

6. 字符串常数

范例6:(string-init-double-quote.c)使用字符串常数的方式初始化

#include<stdio.h>
int main(void)
{
    char s[80] = "main()\n{\n}\n";
    printf("%s", s);
    return 0;
}
字符串的最后面会加一个 '\0' 字元,会自动帮补的,无需自己补。

空字符串

是一个特别的字符串常数,写成 “”。

空字符串的第一个字符就是 '\0',所以它只占1个 字节


7. 字符串读入

char string[10];<pre name="code" class="objc">scanf("%s", string); //由于string本身可以代表地址位置,所以不需要加 & 取址符号,字符串中间 无空格 无换行 无Tab等,无以上东西的才会被读到一个 string 里面去
 


8. 字符指针

片语9:指向一个字符数组的字符指针也能当字符串

char string[80];   // 定义字符串
char *ptr = string;   //指针一开始指向 字符串 的开头

范例10:(char-pointer.c)指向一个字符数组的字符指针

#include<stdio.h>
#include<string.h>
int main(void)
{
    char string[80];
    char *ptr = string;  //指针指向了 string 的开头,ptr和string 就已经是存的同样的地址了。
    int i;
    scanf("%s", ptr);  //写 ptr 和 写 string 都是把 字符串 放到 string 里头
    printf("%s\n", ptr);
    for (i = 0; i < strlen(ptr); i++)
        printf("%c  ", ptr[i]); //如果最开始 ptr 没有知道string的开头部分,而是指到了其他部分,那么 ptr[i]所代表的东西就不太一样,可以做动态调整
    return 0;
}
输入:programming

输出:programming

          p r o g r a m m i n g 


片语11:字符指针这种类别的字符串也可以赋初始值

char *string = "programming";  //这个字符数组是不能改的
编译器会在 只读存储器 中放一个字符数组,初始化成 “programming”, 再将 string 指向这个 字符数组

三种字符串

范例12:(char-pointer-init.c)三种字符串

#include<stdio.h>
#include<string.h>
int main(void)
{
    char str1[80] = "programming";  //如果要打印 str1[88] 这种也是可以打印的
    char str2[] = "programming";   // 如果要打印 str2[28] 这种也是可以打印的
    char *str3 = "programming";
    printf("sizeof(str1) = %d\n", sizeof(str1));<pre name="code" class="objc">    printf("sizeof(str2) = %d\n", sizeof(str2));<pre name="code" class="objc">    printf("sizeof(str3) = %d\n", sizeof(str3));<pre name="code" class="objc">    printf("strlen(str3) = %d\n", strlen(str3));<pre name="code" class="objc">    return 0;<pre name="code" class="objc">}
 
 
 

 
 输出: 

sizeof(str1) = 80

sizeof(str2) = 12

sizeof(str3) = 8 //char *string3 本身是指针,只占8个字节。 我在自己的编译器上,是占了4个字节。

strlen(str3) = 11


9. 常用函数

函数原型14:strlen

int strlen(char *string);  // 并不包括最后的 '/0',只计算了前面的长度

范例16:(my-strlen.c)自己做 strlen

#include<stdio.h>
int my_strlen(char *string)
{
    int i = 0;
    while(i < 80 && string[i] != '\0')
        i++;
    return i;
}

int main(void)
{
    int length;
    char string[80];
    scanf("%s", string);
    printf("%s\n", string);
    length = my_strlen(string);
    printf("%d\n", length);
    return 0;
}

长度 <= 80 的字符串都可以计算长度( = 80 的时候会出现 debug error ,但是可以得到正确答案 : 80 )


函数原型 18:(strcpy-strcat)

char *strcpy(char *destination, char *source)
char *strcat(char *destination, char *source)

strcpy(string copy)复制,source字符串结尾的 '\0' 也会被复制到 destination 字符串

strcat (string concatenation)接尾巴,将第二个字符串复制并接到第一个字符串的后面


范例 19:(string-copy.c)strcpy

#include<stdio.h>
#include<string.h>
int main(void)
{
    char source[100];
    char destination[100];
    scanf("%s", source);
    scanf("%s", destination);
    printf("%s\n", destination);
    strcpy(destination, source);
    printf("%s\n", destination);
    return 0;
}
输入:source  

         destination

输出:destination

          source


范例 20:(string-concat.c)strcat

#include<stdio.h>
#include<string.h>
int main(void)
{
    char source[100];
    char destination[100];
    scanf("%s", source);
    scanf("%s", destination);
    printf("%s\n", destination);
    strcat(destination, source);
    printf("%s\n", destination);
    return 0;
}

输入:source  

         destination

输出:destination

          destinationsource


注:如果 copy 的时候,source过长,destination不够长,这种情况叫做 buffer overrun。source里超出长度的数据就会被破坏。

范例 21:(buffer-overrun.c)

#include<stdio.h>
#include<string.h>
int main(void)
{
    char destination[16];
    char source[80];
    printf("destination at %p\n", destination);
    printf("source at %p\n", source);
    scanf("%s", source);
    printf("source = %s\n", source);
    strcpy(destination, source);
    printf("source = %s\n", source);
    printf("destination = %s\n", destination)
    return 0;
}
输入:

bufferoverrunwillhappen

输出:

destination at 0x7fffc72762d0

source at 0x7fffc72762e0

source = bufferoverrunwillhappen  // 截止到will 的第一个 l 之后,是16 个字符

source = lhappen          // 由于destination 和 source 的地址是接着的,所以在destination 里存放不下的字符就会继续存放在地址紧接的 source里,并且带着source字符串里的                                                 '\n',所以source只引出了lhappen。

destination = bufferoverrunwillhappen

函数原型 22:(strncpy-strncat)

char *strncpy(char *dest, char *source, int i);
char *strncat(char *dest, char *source, int i);

注:strncpy 可由第 3 个参数 i 控制“至多”要复制几个字节的数据,避免缓冲区覆盖

      strncpy 不会自动补充结束字符 '\0', 而是要自己手动添加


范例 23:(buffer-no-overrun.c)strncpy避免超过长度

#include<stdio.h>
#include<string.h>
int main(void)
{
    char destination[16];
    char source[80];
    printf("destination at %p\n", destination);
    printf("source at %p\n", source);
    scanf("%s", source);
    printf("source = %s\n", source);
    strncpy(destination, source, 15);
    destination[15] = '\0';
    printf("source = %s\n", source);
    printf("destination = %s\n", destination)
    return 0;
}
输入:

bufferoverrunwillhappen.

输出:

destination at 0x7fff30ebe3e0

source at 0x7fff30ebe3f0

source = bufferoverrunwillhappen.

source = bufferoverrunwillhappen.

destination = bufferoverrunwi


函数原型 25:(strcmp-strncmp)

int strcmp(char *string1, *string2);
int strncmp(char *string1, *string2, int n);

strcmp:比较两个字符串的大小

strncmp:只比较到第 n 个字节

如果string1 较小,则返回负数

如果string1 较大,则返回正数

如果一样大,则返回 0

比较方法:由第一个字符开始按照 ACSII码的大小开始比较,如果相同则比较第二个字符,知道比出大小或者其中一个字符串结束为止。


范例 26:(string-sort.c)将字符串排序

#include<stdio.h>
#include<string.h>
int main(void)
{
    char zodiac[12][40];
    int i, j;
    char temp[40];

    for (i = 0; i < 12; i++)
        scanf("%s", zodiac[i]);  //把生肖读进来
    for (i = 10; i>= 1; i--)         //冒泡排序法,第一轮是1和2比,2和3比,一次类推,把最大的数推至第11位上去。第二轮把最大的数推至第10位上去...
        for (j = 0; j <= i; j++)
            if(strcmp(zodiac[j], zodiac[j+1]) > 0)
                {
                    strcpy(temp, zodiac[j]);
                    strcpy(zodiac[j], zodiac[j+1]);
                    strcpy(zodiac[j+1], temp);
                }
    for (i = 0; i < 12; i++)
        printf("%s\n", zodiac[i]);
    return 0;
}



范例 27:(string-pointer-sort.c)使用指针数组将字符串排序

优点:变化指针指向的先后位置,即可得到新的排序,而无需对本来存在的存放数据的数组进行重写

#include<stdio.h>
#include<string.h>
int main(void)
{
    char zodiac[12][40];
    char *zptr[12];
    int i;
    int j;
    char *temp;
    for(i = 0; i < 12; i++)
    {
        scanf("%s", zodiac[i]);
        zptr[i] = zodiac[i];
    }
    for(i = 10; i >= 1; i--)
        for (j = 0; j <= i; j++)
            if (strcmp(zptr[j], zptr[j+1]) > 0)
                {
                    temp = zptr[j];
                    zptr[j] = zptr[j+1];  //省去 strcpy 的时间
                    zptr[j+1] = temp;
                }
    for (i = 0; i < 12; i++)
        printf("%s\n", zptr[i]);
    return 0;
}

函数原型 28:(strtok)
char *strtok(char *string, char *delimeters)

strtok (string to token)把第一个字符串参数 string 切成一段一段的 token
token 是第一个字符串中 被第二个字符串参数 delimeters 中的任何字符 所隔开的部分

片语 29: strtok
start = strtok(string, delimeters);
while (start != NULL)
    {
        process string at start;
        start = strtok(NULL, delimeters);
    }

範例 30: (strtok.c) 使用 strtok 將字串切成 token
#include<stdio.h>
#include<string.h>
int main(void)
{
    char delimeters[] = "/";  //以 "/" 作为切分时候的分隔符
    char pathname[40];
    char file[40][40];
    int file_count = 0;
    char *start = pathname;
    int copy_length;
    int i;
    scanf("%s", pathname);
    start = strtok(start, delimeters);    // strtok 的固定使用形式
    while (start != NULL) 
    {
        strcpy(file[file_count], start);
        file_count++;
        start = strtok(NULL, delimeters);
    }
    for(i = 0; i < file_count; i++)
        printf("%s\n", file[i]);
    printf("After strtok pathname becomes %s\n", pathname);
    return 0;
}


如果分割符发生变化,就像这样:
char delimeters[] = "aeiou";

其他代码不变,结果为:










  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值