学习C的到此一游小节

  1. 获取指定长度的字符串,或者说为字符串数组获取用户输入的字符

void get_str(char str[], int len)

{

int i=0, c;

while (i<len)

{

c = getchar();/*这里是防止开始之前用户输入过回车,我们就跳过不处理*/

if (c == ‘\n‘)

{

if (!i)

continue;

str[i] = ‘\0‘;

break;

}

str[i++] = c;

}

if (i == len)

{

if (c != ‘\n‘)

while (getchar() != ‘\n‘)

;

str[i] = ‘\0‘;

}

}

使用举例:

char str[11];//结尾保存’\0’

get_str(str,sizeof(str)-1);

或者使用如下格式

#define NAME_CUT 50

char name[NAME_CUT+1];

get_str(str,NAME_CUT);

 

2.添加双引号的宏

#define F(x) _F(x) 

#define _F(x) #x

使用举例:

在这种场景下

#define NAME_CUT 50

char name[NAME_CUT+1]

我们需要打印name,就可以写成

printf(“%” F(NAME_CUT) ”s\n”,name);

当然真如果出现这种情况,我推荐使用如下宏

#define CUT_FORMAT(x)  _CUT_FORMAT(x)

#define _CUT_FORMAT(x) “%”#x”s”

使用就改为

printf(CUT_FORMAT(NAME_CUT) “\n”,name);

 

3.推荐的链表的声明格式

typedef

struct node

{

......

}

NODE;/*数据类型*/

typedef

struct list

{

NODE data;

struct NODE *next;

}

LIST;/*链表类型*/

使用举例

#define NUM_CUT 10  /*数据中编号最大长度*/

#define NAME_CUT 20 /*数据中姓名最大字符数*/

typedef

struct

{

char num[NUM_CUT + 1];

char name[NAME_CUT + 1];

char sex;

char age;

}

STU;/*数据类型*/

typedef 

struct NODE

{

STU data;

struct NODE *next;

}

LIST;/*链表类型*/

这种写法非常漂亮,容易扩展

 

4.简单的malloc宏,添加了’日志’记录

#define MALLOC(mp) \

{\

if (NULL == (mp = malloc(sizeof(*mp)))) \

{\

fputs("内存分配失败,程序退出......", stderr);\

exit(EXIT_FAILURE); \

}\

}

使用举例

假设存在如下环境

#define NUM_CUT 10  /*数据中编号最大长度*/

#define NAME_CUT 20 /*数据中姓名最大字符数*/

typedef

struct

{

char num[NUM_CUT + 1];

char name[NAME_CUT + 1];

char sex;

char age;

}

STU;/*数据类型*/

STU *stu;

那么为stu开辟一个空间只需要添加下面的宏语句.

MALLOC(stu);//显然它也存在一个C关于malloc的通病,就是内存不足时,再打印文本也是无法执行的.但是它的原意是日志记录,虽然不一定会记录成功.

再扩展一下MALLOC如下:

#define MALLOC(var) if(!(var=malloc(sizeof(*var)))){\

fputs("内存申请失败,程序退出中...\n",stderr);\

exit(EXIT_FAILURE);\

}

5.文件打开的简单宏,添加了’日志’记录

#define FOPEN(file,path,type) \

{\

if (NULL == (file = fopen(path, type)))\

{\

fputs("内存不足程序退出中", stderr);\

exit(EXIT_SUCCESS);\

}\

}

举例

FILE *file;

FOPEN(file,”work_log.rec”,”rb”);

 

6.在控制台上清除输入缓存

while (getchar() != ‘\n‘)

;

举例

譬如存在如下语句

int c,num;

printf(“请输入一个整数:”);

scanf(“%d”,&num);

printf(“请再输入一个字符:”);

c=getcahr();

在上面的scanf和getchar之间就存在输入缓存问题,默认getchar()接收的值为’\n’.

这样c获取的值不是我们事先预估的

解决办法就是在二者之间加上

While(getchar()!=’\n’)

;

扩展一下,更加严格的应该是这段代码

int ch;

While((ch=getchar())!=EOF&&ch!=’\n’)

;

上面的情况是允许用户使用退出getchar()的操作,getchar这个函数在接收错误的时候返回-1,其实就是EOF文件结束标志宏.在window上可以按下Ctrl+Z,Linux上可以按下Ctrl+D达到让getchar函数返回-1的效果,结束当前的输入.

在这里,我采用的设计思路是,要么关闭当前程序,要么就必须输入完整.关键看程序员的想法吧!

 

7.关于scanf函数获取用户输入值的一种简便用法

while (printf(......)

,scanf(......)!=......||......)

{

while (getchar() != ‘\n‘)

;

puts(......);

}

举例

例如存在如下要求,需要接收用户输入的一个数,这个数必须小于250,大于0

代码如下:

int num;

while(printf(“请输入一个大于0小于250的整数:”),

scanf(“%d”,&num)!=1||num<=0||num>=250)

{

while(getchar()!=’\n’)

;

    puts(“输入错误,请按照提示重新操作!”);

}

当然有时嫌它代码重复,可以定义如下简单宏

#define CLEAR 
while (getchar() != ‘\n‘)/*清除缓存*/

#define CLEARANDMSG {CLEAR;puts("输入出错,请按照提示重新操作!");}

使用的话就可以写成

int num;

while(printf(“请输入一个大于0小于250的整数:”),

scanf(“%d”,&num)!=1||num<=0||num>=250)

      CLEARANDMSG;

但是上面的代码很丑,关键看自己怎么看了.

 

8.关于printf输出格式串%*的用法

#define NAME_CUT 50

char name[NAME_CUT+1];

printf("%*s\n",NAME_CUT,name);

举例

对于这个技巧,是为了避免一些错误,例如如果name数组中没有’\0’,输出采用%s则会使输出内容不可控.还有一点就是它也统一控制了姓名打印对齐的格式.

 

9.比较古老的关于结构体空间声明技巧

typedef 

struct node

{

int num;

char name[];

}

NODE;

举例

上面是C99定义包含空数组的结构体,在比较老的时候还存在

typedef

struct node

{

int num;

char name[1]; 

}

NODE;

typedef

struct node

{

int num;

char name[0]; 

}

NODE;

上面两种模式,思路都一致.声明的时候可以写成

int n=10;

NODE node=malloc(sizeof(NODE)+n);

达到可变数组的目的.(注结构体中 char name[]和char *name,是不同的)

 

10.高级简单一点的FOPEN宏的实现

#define FOPEN(file,path,type) \

FILE *##file;\

if (NULL == (##file = fopen(path, type)))\

{\

fputs("内存不足程序退出中", stderr);\

exit(EXIT_SUCCESS);\

}

举例

如果想将worker_one.rec文件内容复制到worker_two.rec文件中,

创建并打开文件对象的代码就是:

FOPEN(worker_one_file,”worker_one.rec”,”rb”);

FOPEN(worker_two_file,”worker_one.rec”,”wb”);

详细完整的代码如下:

FOPEN(wo_file,”worker_one.rec”,”rb”);

FOPEN(wt_file,”worker_one.rec”,”wb”);

for(int c;(c=fgetc(wo_file))!=EOF;fputc(c,wt_file))

;

fclose(wo_file);

fclose(wt_file);

 

12.比最牛逼的FOPEN宏更牛逼的USING_FILE宏

#define USING_FILE(file,path,type,code) \

{\

FILE *##file;\

if (NULL == (##file = fopen(path, type)))\

{\

fputs("内存不足程序退出中", stderr);\

exit(EXIT_FAILURE);\

}\

##code;\

fclose(##file);\

}

举例

仍然处理”将worker_one.rec文件内容复制到worker_two.rec文件中”,

这个问题,代码如下

USING_FILE(wo_file,”worker_one.rec”,”rb”,

{

USING_FILE(wt_file,”worker_two.rec”,”wb”,

{

for(int c;(c=fgetc(wo_file))!=EOF;fputc(c,wt_file))

;

});

});

推荐加上括号美观(也推荐括号另起一行),是不是有一种函数式编程的感觉呢.

哈哈,也许你也被我坑了,其实上面的代码只是一厢情愿.对于第二个参数,宏只当

字符串处理.不会再替换了,把它当成宏.正确的做法还需要求助FOPEN这个宏.混搭.

#define FOPEN(file,path,type) \

FILE *##file;\

if (NULL == (##file = fopen(path, type)))\

{\

fputs("内存不足程序退出中", stderr);\

exit(EXIT_SUCCESS);\

}

正确的代码为:

USING_FILE(wo_file,”worker_one.rec”,”rb”,

{

FOPEN(wt_file,”worker_two.rec”,”wb”);

for(int c;(c=fgetc(wo_file))!=EOF;fputc(c,wt_file))

;

    fclose(wt_file);

});

这里也看的出来USING_FILE这个宏使用时不能嵌套,一个优美的鸡肋.表达了某个C程序

员的美好想法吧.(宏还存在一个问题,宏参不能太长,否则有的编译器会出现警告.后导致错误,再扯一下,解决这个错误的方法就是将代码块封装为函数.)

 

13 比牛逼的MALLOC宏更彻底的TYPE_MALLOC宏

#define TYPE_MALLOC(type,var) ;\

##type *##var;\

if(NULL==(##var=malloc(sizeof(##type))))\

{\

fputs("内存申请失败,程序退出中...",stderr);\

exit(EXIT_FAILURE);\

}

举例

如果你想声明一个int *hoge;

那么可以写成 TYPE_MALLOC(int,hoge);

同样如果你有一个这样一个结构体

typedef 

struct node

{

int num;

char *name;

}

NODE;

如果你想声明一个结点结构体指针变量,可以写成

TYPE_MALLOC(NODE,pnode);

对于这个宏适用于第一次声明指针变量并想分配空间的情况.上面的宏不知道有没有人好奇,为什么开始有个;.其实这个;是一个空语句.主要是为了解决,这样的错误情况”宏定义不能以##开头”.当然也可以用其它方式解决这个错误,例如添加”{}”,等等.宏很吊,有点像C中的第二种语法,生成代码的代码.上面的宏推荐写成这样:

#define POINTER_MALLOC(type,var) type *var;\

if(NULL==(var=malloc(sizeof(type)))){\

fputs("内存申请失败,程序退出中...",stderr);\

exit(EXIT_FAILURE);\

}

再次扩展一下,其实下面的宏用法最多:

#define POINTER_MALLOC(type,var,num) type *var;\

if(NULL==(var=malloc(sizeof(type)*num))){\

fputs("内存申请失败,程序退出中...",stderr);\

exit(EXIT_FAILURE);\

}

上面那个宏现在也不是很标准,采用下面这个宏,但是这个宏目前依赖stdio.h和stdlib.h文件.代码如下:

#define POINTER_MALLOC(type,var,num) type *var;\

if(!(var=malloc(sizeof(type)*(num)))){\

fputs("内存申请失败,程序退出中...\n",stderr);\

exit(EXIT_FAILURE);\

}

外加一个宏吧:

#define TYPE_MALLOC(type,var) type *var;\

if(!(var=malloc(sizeof(type)))){\

fputs("内存申请失败,程序退出中...\n",stderr);\

exit(EXIT_FAILURE);\

}

 

 

14.简化scanf函数的宏

#define SAFETY_SCANF(print_code,scanf_code) \

while(printf(##print_code),##scanf_code)\

{\

while(getchar()!=‘\n‘)\

;\

puts("输入错误,请按照提示重新操作!");\

}

举例

有时上面代码就是重复劳动,而且不好封装成函数.例如原先

想写成这样代码:

int mouth;

while(printf(“请输入购票的月数:”),

scanf(“%d”,mouth)!=1||mouth<1||mouth>12)

{

while(getchar()!=’\n’)

;

puts(“输入错误,请按照提示重新操作!”);

}

现在使用SAFETY_SCANF宏的写法就是这样了

SAFETY_SCANF(“请输入购票的月数:”

,scanf(“%d”,mouth)!=1||mouth<1||mouth>12);

感觉不好,上面宏应该改写成这样

#define SAFETY_SCANF(print_code,scanf_code) \

while(##print_code,##scanf_code)\

{\

while(getchar()!=‘\n‘)\

;\

puts("输入错误,请按照提示重新操作!");\

}

这样处理这种情况就容易了:

#define N 10

float score[N];

for(int i=0;i<N;i++)

SAFETY_SCANF(printf(“请输入第%d个学生总分:”,i+1)

,scanf(“%f”,score+i)!=1||score[i]<0); 

这样几乎朗阔了各种情况

再扩展一下,如果觉得printf没必要,还是可以扩展成如下形式:

#define SAFETY_SCANF(scanf_code,...) \

while(printf(__VA_ARGS__),##scanf_code)\

{\

while(getchar()!=‘\n‘)\

;\

puts("输入错误,请按照提示重新操作!");\

}

这样上面调用的方式就变成:

#define N 10

float score[N];

for(int i=0;i<N;i++)

SAFETY_SCANF(scanf(“%f”,score+i)!=1||score[i]<0,“请输入第%分:”,i+1); 

再扩展一下,如果想写成函数如何写,不好意思,就算用上更高级特性都无法做的比宏好.

再次扩展一下,上面宏改成这样,会更好.

#define SAFETY_SCANF(scanf_code,...) while(printf(__VA_ARGS__),scanf_code)\

{\

while(getchar()!=‘\n‘);\

puts("输入出错,请按照提示重新操作!");\

}\

while(getchar()!=‘\n‘)

这样的话,更简单而且也不会给下一次输入带来输入缓存问题.强力推荐最后一个最安全的写法.(其实上面宏能跑起来,编译器的贪婪模式帮了很大忙.)实战的时候推荐这么写:

#define SAFETY_SCANF(scanf_code,...) {while(printf(__VA_ARGS__),scanf_code){\

while(getchar()!=‘\n‘);\

puts("输入出错,请按照提示重新操作!");\

}while(getchar()!=‘\n‘);}

要不再扩展一下,如何用函数来模拟.例如模拟下面这段代码

int mouth;

int cut = 3;

SAFETY_SCANF(scanf("%d",&mouth)!=1||mouth<1||mouth>12,"请输入购买的第%d张票的月份:",cut+1);

如果用函数来模拟,需要做的东西有点多.完整的code如下,

#include <stdio.h>

#include <stdarg.h>

#include <stdbool.h>

 

typedef bool(*judge_scanf)(const char *fmt, ...);

 

bool judge_mouth(const char *fmt_scf, int *pm);

void safety_scanf(judge_scanf judge, const char *fmt_scf, int *pm, const char *fmt, ...);

 

int main(void)

{

int mouth;

int cut = 3;

safety_scanf(judge_mouth, "%d", &mouth, "请输入购买的第%d张票的月份:", cut + 1);

return 0;

}

 

bool judge_mouth(const char *fmt, int *pm)

{

return scanf(fmt, pm) != 1 || *pm<1 || *pm>12;

}

 

void safety_scanf(judge_scanf judge, const char *fmt_scf, int *pm, const char *fmt, ...)

{

va_list var;

va_start(var, fmt);

vprintf(fmt, var);

while (judge(fmt_scf, pm))

{

while (getchar() != ‘\n‘)

;

puts("输入出错,请按照提示重新操作!");

va_start(var, fmt);

vprintf(fmt, var);

}

va_end(var);

while (getchar()!=‘\n‘)

;

}

上面只是最符合上面情况的模拟,想完全做到,我感觉需要做很多工作.就从这里打住吧!

 

15.USING_FILE宏的再次重生,更加屌爆了

#define USING_FILE(file,path,type,code) {\

FILE *file;\

if (NULL == (##file = fopen(path, type)))\

{\

fputs("内存不足程序退出中", stderr);\

exit(1);\

}\

code;\

fclose(file);\

}

举例

例如我们需要新建一个output.txt文件,并写入”Hello World!”字符,并换行,之后读取output.txt文件复制到new_output.txt文件中.

代码如下:

USING_FILE(file, "output.txt", "wb+", {

fprintf(file,"Hello World!\r\n");

fseek(file, 0l,SEEK_SET);

USING_FILE(new_file, "new_output.txt", "wb", {

int c;

while (EOF != (c = fgetc(file)))

fputc(c, new_file);

});

});

return 0;

什么都不说了,扩展一下,如果宏中存在#,##,那么宏就不可以嵌套展开.现在推荐大家采用如上写法格式,用以区别同正规代码格式.

 

16 字符串查找字串,返回第一次查找到的索引

int str_index(const char *source,const char *target)

{

const char *cpy, *tar,*st=source;

do

{

while (*st&&*st != *target)/*先过滤一些完全不匹配的字符*/

st++;

for (cpy = st, tar = target; *cpy&&*cpy == *tar; cpy++, tar++)

;/*查找看是否完全匹配*/

if (!*tar)

return st - source;

} while (*st++);

return -1;

}

举例

使用起来比较简单,简单代码如下:

//查找字符串中所有字串出现的位置

const char *s = "I love my wife", *t = " ";

int idx,len=strlen(s),cut=0;

printf("源串[%s]中出现字串[%s]的位置是:",s,t);

do/*下面打印所有空格出现的位置*/

{

if (-1 == (idx = str_index(s + cut, t)))

break;

cut += idx+1;

printf("%d ", cut - 1);

} while (cut<len);

putchar(‘\n‘);

//这个函数用只能是凑合着用

17

举例

学习C的到此一游小节,布布扣,bubuko.com

时间: 2024-08-04 15:48:58

学习C的到此一游小节的相关文章

Python 3 学习的第八天——深浅拷贝以及函数

Python 学习的第八小节 写此博客 是为了激励自己,并且将自己的心得以及遇到的问题与人分享 一.学习笔记 1.深浅拷贝 浅copy-- copy.copy()  #shallow copy 使用 b = a.copy() 这样b是对a进行了浅copy  包含了浅copy的一切特点 特点:只copy"一层"  这个一层是指(假如a[ [ 1 , 2 ] , 3 , 4 ])这样,b copy后只能修改 b[1].b[2]的值cai不会影响 a 的值,如果b修改了列表中元素的值,那么就

机器学习基石第三讲:types of learning

博客已经迁移至Marcovaldo's blog (http://marcovaldong.github.io/) 刚刚完成机器学习基石的第三讲,这一讲主要介绍了机器学习的分类,对何种问题应该使用何种机器学习方法.将笔记整理在下面. Learning with Different Output Space 前面讲的信用卡发放问题是一个是非题,也就是说最后的输出只有两种,是一个二元分类(binary classification).下图中给出了更多的二元分类问题的例子,对于这类问题我们要做的就是找

三、使用Maven构建简单的java项目

前边,我刚搭建了Maven环境,还有给大家推荐了学习资源,这个小节,我们来就来,,简单的玩玩maven. 1.所需工具: 1.Eclipse     2.apache-maven-3.3.9   3.JDK  1.7 2.命令构建简单的java项目 (1)运行cmd 切换盘符到你指定的文件夹下,如图 (2)键入以下命令: mvn archetype:generate -DgroupId=com.software.wg -DartifactId=FirstMvn -DarchetypeArtifa

寒假作业2

寒假作业2 GitHub仓库地址:Destr 慕课在线学习 课程 我选择的是西安交通大学计算机程序设计(C++),授课老师赵英良. 原因 这门课程从C++基础开始讲起,较好的完成了从C到C++的过度,而且西安交大也是一所知名的高校,可以使我较好的在寒假期间完成C++的学习 课程目录 第1周 程序设计与C++概述 第2周 简单信息的表示和基本运算 第3周 运算的流程控制 第4周 复杂信息的表达与处理 第5周 问题的模块化求解 第6周 问题的模块化求解(2)--特殊函数 第7周 按址操作(1)--指

16-CoreData之多表关联(存储自定义数据模型)

多表关联 1.1-简介 什么是多表关联 在处理数据库的关系中,无非只有三种关系 一对一:一个老师只能在一个教室上课,不可能同时在两个教室上课 一对多:一个教室可以有多个学生,但一个学生只能在一个教室 多对多:一门学科可以有多个学生,一个学生也可以学习多门学科 本小节我们主要学习一对多的关联,通过学习一对多来举一反三其他两种关系 1.2-如果在模型文件中对两个实体之间进行关联? 给教室关联学生 给学生关联教室 Type: toOne:只指向一个对象(可用于某一个属性是自定义数据模型) toMany

【转】Java8 Stream 流详解

  当我第一次阅读 Java8 中的 Stream API 时,说实话,我非常困惑,因为它的名字听起来与 Java I0 框架中的 InputStream 和 OutputStream 非常类似.但是实际上,它们完全是不同的东西. Java8 Stream 使用的是函数式编程模式,如同它的名字一样,它可以被用来对集合进行链状流式的操作. 本文就将带着你如何使用 Java 8 不同类型的 Stream 操作.同时您还将了解流的处理顺序,以及不同顺序的流操作是如何影响运行时性能的. 我们还将学习终端

event事件学习小节

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>event事件</title> </head> <body> <script> document.onclick=function(ev){//谷歌火狐的写法,IE9以上支持,往下不支持: var e=ev; consol

HBase学习小节v1.2

1. HBase 一个构建在HDFS上的高可靠.高性能.面向列.可伸缩.分布式列存储开源数据库,主要用于存储海量数据,同时使用mapreduce处理HBase中的数据,利用zookeeper作为协同服务.读写相对简单,不支持条件查询 2. HBase与HDFS对比 都具有良好的容错性和扩展性 HDFS适合批处理场景,但是不支持数据随即查找,不适合增量数据处理,不支持数据更新 3. HBase的特点: 海量数据:可支持上百万列,分成多个region 无模式:每行都有一个可排序的主键和任意多的列,列

python学习---第一小节

运行首个程序hello_world.py #_*_coding:utf-8_*_ print("Hello world!") 输出结果: Hello world! 变量 #_*_coding:utf-8_*_ name = "beyoungt" #_*_coding:utf-8_*_ name = "beyoungt" print(name) name = "abby" print(name) 输出结果: beyoungt a