黑马程序员——C语言基础篇---宏定义、数组、字符串和函数
2015-04-14 16:35
295 查看
------Java培训、Android培训、iOS培训、.Net培训 期待与您交流!-------
本篇将要通过一道简单的C语言小程序题来引入今天的主题:宏定义、数组、字符串和函数。下面先来看看我们的题目吧
* 从键盘输入一大堆字符串,统计A、B、C、D的出现次数,最后出现次数由高到低输出字母和出现次数。
这道题在我看来可以分为三大功能模块:输入字符串、统计次数、排序并输出,这就决定了我们主函数的结构
先来看看代码头
include是包含程序中需要用到的函数的头文件
关于define这是预处理指令中的宏定义
本题使用的是它不带参数的定义
#define 宏名 字符串(注意:结尾不用【;】)
也就是在程序中我们使用宏名来表示右面的字符串内容,宏定义其实就是在程序运行前编译的时候,将宏名替换成右边的字符串,再进行后续操作,个人感觉这是最常见的一种使用方式,说到这里再介绍另外一种宏定义的使用方法:带参数的宏定义
下面的代码与题目无关,只是作为一个宏定义使用方法的补充
结果是4
但是这个宏定义是有问题的,如果变成 TEST(2+2),结果却不是16,而是8,这是因为宏定义是是纯粹的替换,而不能像函数一样能帮你做计算,TEST(2+2)被替换成2+2 * 2+2,结果自然是8,所以在定义时需要给变量套上 (),但这样也还是不够的,还要给结果也加上()
改进后的代码 (下面的代码与题目无关)
从这段代码中就可以找到我们今天的主题:数组、字符串和函数,下面我们分开来简单讲讲他们的用法。
数组的声明:类型 数组名[下标];
比如上述的int count
= {0};
关于数组,需要注意的有以下几个点:
(1)数组名表示数组首元素的地址(这在把数组当做参数传给函数时要注意,上述代码中的Sort(count),就是这种用法)。
(2)数组的初始化只能在定义数组的时候,上述代码中的int
count
= {0};就是如此,如果把这段代码拆开编程下面的代码
int count
;
count
={0};
这样是错误的,编译的时候就会报错,一定要注意了。
(3)下标的位置只能用常量而不能用变量,大家一定注意到这段代码了int
count
= {0}; ,这不是用了变量N吗?注意:这是上面的宏定义,值是4,而非变量,如果定义一个变量作为下标使用,编译的时候也是会报错的,但宏定义没问题,它在程序运行时已经是个常量了。
(4)C语言中数组存储的只能是统一类型的数据
下面的代码与题目无关
字符串的定义,必须是 char * ,实际上就是个指针,而非字符数组,比字符数组多了【'\0'】,在以%s输出字符串的时候,遇到‘\0’就停止输出,如果定义字符数组
char input[100] = {}; ,由于没有‘\0’,会从起始地址开始一个一个输出字符,直到遇见'\0'为止。但如果这么写,char
input[100] = {‘\0’},这样没问题了。
本段代码中,这一句代码 char *input = (char *) malloc(M); 这是因为如果定义一个字符串变量来接收从屏幕输入的字符串,它此时还没有分配存储空间,无法使用,所以通过这句给它分配存储空间,这样后面的使用就完全没有问题了,因为malloc这个函数,包含了stdlib.h。
还有一点需要注意的:
计算字符串长度(指的是字节数,而非字数,比如1个汉字占3个字节,它的长度是3而不是1)
sizeof() 输出字符串内所有的字节数,包括'\0'
strlen() 输出字符串内所有的字符数,不包括'\0'
函数在使用前必须有其声明,习惯上会写到主函数前面,如果整个函数都在主函数前面就不需要单独声明了
默认情况下,是外部函数(extern),也就是其他文件也可以使用,如果限制于本文件使用需要在void前面加上关键字 static
void 表示无返回值,如果有返回值的话需要更改成int 、double等需要的类型
()内表示需要传入的参数,是实参,局部变量
下面的代码与题目无关
还有一种是传地址,也就是我们练习中的代码
这里的两个参数都是进行的地址传递,也就是将主函数中的字符串和数组的地址当做形参传了进来,所以在函数中修改其值是可以的。
这段小代码中用到了while(循环结束的条件)循环结构和if分支结构,在统计次数时有一个小的技巧,那就是数组的下标使用了 *in - 'A' ,这样不必用分支语句去判断再统计了,c[0]表示A的次数,c[1]表示B的次数,c[2]表示C的次数,c[3]表示D的次数,c指向count,所以此时count里的值也是对应的。这里对输入字符串的遍历是通过指针,从首地址开始,利用
*in 访问每一个字符,in++表示指针移动到下一个字符。
在这里有一点需要注意,本段代码中的c 如果利用sizeof来计算的话,与我们以前在主函数中调用结果不同,得到的是8字节,因为这里已经变成指针了,所以要使用sizeof计算数组长度的话,最好是在主函数中进行。
这是最后一段代码了,里面有用到结构体,结构体实际上就是将不同类型的数据集合成一个复杂数据类型,里面可以是基本数据类型、指针、也可以嵌套结构体。
对于结构体所占据的存储空间,采用补齐算法(对齐算法),也就是结构体占据的存储空间是其定义中最大变量长度的倍数。比如上面代码所定义的结构体NewSort,本来它的长度应该是:1(char)+
4(int)=5字节,但是由于补齐算法,它会补成int型字节的倍数,也就是8字节。
struct NewSort out
;这一句是利用上面的结构体声明创建一个用于输出的结构体数组,然后利用for循环语句对其进行赋值。
最后的冒泡排序是利用了双重嵌套循环,从首元素开始,将它后面所有的数据与它进行比较,如果后面的数据比当前的数据大,那么两个元素进行交换。
最后,将排序后的数组打印。
这里有一点我自己的习惯,重新定义一个用于输出的数组是为了保障输入的值不变,也就是数据源不会改变,当然,输入和输出用同一个数组在实现上是完全没有问题的。
本篇将要通过一道简单的C语言小程序题来引入今天的主题:宏定义、数组、字符串和函数。下面先来看看我们的题目吧
* 从键盘输入一大堆字符串,统计A、B、C、D的出现次数,最后出现次数由高到低输出字母和出现次数。
这道题在我看来可以分为三大功能模块:输入字符串、统计次数、排序并输出,这就决定了我们主函数的结构
先来看看代码头
<strong>#include <stdio.h> #include <stdlib.h> #define M 100 #define N 4</strong>
include是包含程序中需要用到的函数的头文件
关于define这是预处理指令中的宏定义
本题使用的是它不带参数的定义
#define 宏名 字符串(注意:结尾不用【;】)
也就是在程序中我们使用宏名来表示右面的字符串内容,宏定义其实就是在程序运行前编译的时候,将宏名替换成右边的字符串,再进行后续操作,个人感觉这是最常见的一种使用方式,说到这里再介绍另外一种宏定义的使用方法:带参数的宏定义
下面的代码与题目无关,只是作为一个宏定义使用方法的补充
#include <stdio.h> #define TEST(a) a * a int main() { int r = TEST(2+2); printf("%d\n",r); }
结果是4
但是这个宏定义是有问题的,如果变成 TEST(2+2),结果却不是16,而是8,这是因为宏定义是是纯粹的替换,而不能像函数一样能帮你做计算,TEST(2+2)被替换成2+2 * 2+2,结果自然是8,所以在定义时需要给变量套上 (),但这样也还是不够的,还要给结果也加上()
改进后的代码 (下面的代码与题目无关)
#include <stdio.h> #define TEST(a) ((a) * (a)) int main() { int r = TEST(5+5)/TEST(2+3);//这样可以测出宏定义结果的正确性 printf("%d\n",r); }回到我们的题目,下面是main函数中的代码,对应上面所述,分成了三大模块
int main() { int count = {0}; //定义接收从屏幕输入的字符串变量,并分配存储空间 char *input = (char *) malloc(M); printf("请输入字符串:\n"); //gets()函数会报warning,但是这里要接收空格,所以还是使用了 gets(input); //调用统计函数 Statistics(input,count); //调用排序函数 Sort(count); return 0; }
从这段代码中就可以找到我们今天的主题:数组、字符串和函数,下面我们分开来简单讲讲他们的用法。
1.数组
数组的存储实际上就是数据结构中所说的线性表,也就是从首地址开始,每个数组元素依次向后存储。数组的声明:类型 数组名[下标];
比如上述的int count
= {0};
关于数组,需要注意的有以下几个点:
(1)数组名表示数组首元素的地址(这在把数组当做参数传给函数时要注意,上述代码中的Sort(count),就是这种用法)。
(2)数组的初始化只能在定义数组的时候,上述代码中的int
count
= {0};就是如此,如果把这段代码拆开编程下面的代码
int count
;
count
={0};
这样是错误的,编译的时候就会报错,一定要注意了。
(3)下标的位置只能用常量而不能用变量,大家一定注意到这段代码了int
count
= {0}; ,这不是用了变量N吗?注意:这是上面的宏定义,值是4,而非变量,如果定义一个变量作为下标使用,编译的时候也是会报错的,但宏定义没问题,它在程序运行时已经是个常量了。
(4)C语言中数组存储的只能是统一类型的数据
2.字符串
字符串,顾名思义“一串”字符,是字符流,以'\0'结尾,'\0'的作用就是用来标识字符串结束了,这点在以%s输出字符串的时候很有用。下面的代码与题目无关
int main() { char *input = ""; input = "hello world"; printf("%s\n",input); return 0; }
字符串的定义,必须是 char * ,实际上就是个指针,而非字符数组,比字符数组多了【'\0'】,在以%s输出字符串的时候,遇到‘\0’就停止输出,如果定义字符数组
char input[100] = {}; ,由于没有‘\0’,会从起始地址开始一个一个输出字符,直到遇见'\0'为止。但如果这么写,char
input[100] = {‘\0’},这样没问题了。
本段代码中,这一句代码 char *input = (char *) malloc(M); 这是因为如果定义一个字符串变量来接收从屏幕输入的字符串,它此时还没有分配存储空间,无法使用,所以通过这句给它分配存储空间,这样后面的使用就完全没有问题了,因为malloc这个函数,包含了stdlib.h。
还有一点需要注意的:
计算字符串长度(指的是字节数,而非字数,比如1个汉字占3个字节,它的长度是3而不是1)
sizeof() 输出字符串内所有的字节数,包括'\0'
strlen() 输出字符串内所有的字符数,不包括'\0'
3.函数
函数其实就是将一段功能代码封装,需要使用的时候传入(或不传)参数调用即可函数在使用前必须有其声明,习惯上会写到主函数前面,如果整个函数都在主函数前面就不需要单独声明了
//统计函数的声明 void Statistics(char *in,int c[]); //排序函数声明 void Sort(int c[]);
默认情况下,是外部函数(extern),也就是其他文件也可以使用,如果限制于本文件使用需要在void前面加上关键字 static
void 表示无返回值,如果有返回值的话需要更改成int 、double等需要的类型
()内表示需要传入的参数,是实参,局部变量
下面的代码与题目无关
void test(int a) { }传入的参数是基本数据类型,这是进行了值传递,既只是把原来变量的值传递给实参,改变这个值,原来变量的值并不会被修改。
还有一种是传地址,也就是我们练习中的代码
//统计函数:从输入的字符串中,统计A、B、C、D的出现次数 void Statistics(char *in,int c[]) { //遍历字符串 while (*in != '\0') { //统计A、B、C、D的出现次数 if (*in >= 'A' && *in <= 'D') { c[*in - 'A']++; } in++; } }
这里的两个参数都是进行的地址传递,也就是将主函数中的字符串和数组的地址当做形参传了进来,所以在函数中修改其值是可以的。
这段小代码中用到了while(循环结束的条件)循环结构和if分支结构,在统计次数时有一个小的技巧,那就是数组的下标使用了 *in - 'A' ,这样不必用分支语句去判断再统计了,c[0]表示A的次数,c[1]表示B的次数,c[2]表示C的次数,c[3]表示D的次数,c指向count,所以此时count里的值也是对应的。这里对输入字符串的遍历是通过指针,从首地址开始,利用
*in 访问每一个字符,in++表示指针移动到下一个字符。
在这里有一点需要注意,本段代码中的c 如果利用sizeof来计算的话,与我们以前在主函数中调用结果不同,得到的是8字节,因为这里已经变成指针了,所以要使用sizeof计算数组长度的话,最好是在主函数中进行。
//排序函数:由高到低排序,并输出 void Sort(int c[]) { //定义排序的结构体类型 struct NewSort { char c; int times; }; //定义结构体变量 struct NewSort out ; //对结构体变量进行赋值 for (int i = 0; i < N; i++) { out[i].c = 'A' + i; out[i].times = c[i]; } //由大到小排序(冒泡排序) for (int i = 0; i < N; i++) { for (int j = i + 1; j < N; j++) { if (out[j].times > out[i].times) { struct NewSort t; t = out[j]; out[j] = out[i]; out[i] = t; } } } //输出排序后的字母及其出现次数 for (int i = 0; i < N; i++) { printf("%c\t%d\n",out[i].c,out[i].times); } }
这是最后一段代码了,里面有用到结构体,结构体实际上就是将不同类型的数据集合成一个复杂数据类型,里面可以是基本数据类型、指针、也可以嵌套结构体。
对于结构体所占据的存储空间,采用补齐算法(对齐算法),也就是结构体占据的存储空间是其定义中最大变量长度的倍数。比如上面代码所定义的结构体NewSort,本来它的长度应该是:1(char)+
4(int)=5字节,但是由于补齐算法,它会补成int型字节的倍数,也就是8字节。
struct NewSort out
;这一句是利用上面的结构体声明创建一个用于输出的结构体数组,然后利用for循环语句对其进行赋值。
最后的冒泡排序是利用了双重嵌套循环,从首元素开始,将它后面所有的数据与它进行比较,如果后面的数据比当前的数据大,那么两个元素进行交换。
最后,将排序后的数组打印。
这里有一点我自己的习惯,重新定义一个用于输出的数组是为了保障输入的值不变,也就是数据源不会改变,当然,输入和输出用同一个数组在实现上是完全没有问题的。
相关文章推荐
- 黑马程序员_C语言的函数、数组、字符串
- 黑马程序员——C语言基础(五)数组与字符串
- 黑马程序员——C语言基础---复杂数据类型(数组,字符串)
- 黑马程序员--学习笔记--一个字符串、数组、函数综合运用的小练习
- 黑马程序员——c语言基础:数组和字符串
- C语言基础知识之(六):数组-----数组类型、定义数组、读取数组、排序数组、字符串数组函数
- C 语言 实现 字符串 分割 函数(返回"二维字符数组",及分割后的字符数组的长度)
- 黑马程序员————数组,字符串,函数,指针
- 黑马程序员 _4 C语言基础 数组,指针,字符串
- 黑马程序员-C语言基础八:数组、字符串
- day05(补) 字符数组 字符串操作函数 宏定义
- 黑马程序员——C语言基础--数组和字符串
- 黑马程序员_java的语言基础组成最终篇(对第四课函数和数组的总结)
- C语言学习5-1:字符加法,字符串操作函数编写,查找数组中第二大的数
- 黑马程序员-iOS基础-C语言基础(四)内存剖析、数组、字符串
- 黑马程序员----C 语言学习笔记之数组指针与字符串指针
- 黑马程序员——OC语言日志——NSString字符串的比较函数
- 黑马程序员——Java语言基础:程序流程控制、函数、数组
- 黑马程序员——零基础学习iOS开发——06 字符串、指针、指针和数组、指针和字符串、指针和函数
- 黑马程序员—【Java基础篇】之语言基础———语句和函数