C语言编程常见技巧分享

简介: C语言编程常见技巧

本文章根据《算法竞赛入门经典(第二版)》一书整理。。。

    第一章 程序设计入门
  printf 语句控制输出小数位数或总长度
      printf("%.3f\n",8.0/5.0)    //小数位数为3。
      printf("%.f\n",3,8.0/5.0)    //用3来代替 ,用来动态指定小数位数
      printf("%3d",xxx)      //指定输出宽度为3
      printf("%-3d",xxx)      //左对齐,邮编填补空格
      printf("%03d",xxx)      //指定宽度,并用0填补左边空缺
  求多位数各个位上的数
    while(x > 0){
      y = x % 10;    //y分别为个位、十位、百位等
      x /= 10;
    }
  交换变量a,b的值  
    方法一:借用第三方变量
      int a,b,temp;
      temp = a;
      a = b;
      b = temp;
    方法二:不借用第三方变量
      1)使用加减法(和不变)//限制:只有定义了加减法的数据类型才可以使用。
        int a,b;
        a = a +b;
        b = a - b;
        a = a - b;
      2)使用异或运算(^)
        a = a ^ b;  
   b = a ^ b;
   a = a ^ b;
        原理: x^x == 0; x^0 == x;
        首先用a保存了a^b的值,再用b = a ^ b = (a^b)^b=a^b^b=a^(b^b)=a^0=a,这样实现了b = a;
        接着又用a = a^b = (a^b)^b(第一个b还是原来的b,而第二个b已经是a的值)= (a^b)^a = a^a^b = 0^b = b,这样实现交换。
  C语言中,0为假,其余皆为真。
    第二章 循环结构程序设计
  四舍五入
    floor(x + 0.5)
  
  continue和break
    continue进行下一次循环。break则是跳出当前循环体。
整数溢出
    int型一般为32位整数,取值范围为-2^31~2^31-1,即-2147483648~2147483647
  while (先判断后计算)
do while (先计算一次,后判断)  
  scanf判断输入结束:
    windows:输入完毕后,先按Enter键,再按Ctrl+Z键,最后在按Enter键。之所以这么复杂是因为scanf语句的输入格式中空格,TAB,回车符都不意味着输入结束。
    Linux:输入完毕后按下Ctrl+D即可。
    while(scanf("%s",&a) == 1 )    //保证输入不为空(有没有==1,意义不大吧。。。。)
    while(~scanf("%d",&m,&n))  等同于:while (scanf("%d%d",&m,&n)!=EOF)
     //代码效果参考:http://www.zidongmutanji.com/bxxx/553910.html

      //EOF定义为-1。~是按位取反,-1十六进制补码表示为0x ffffffff,f是二进制的1111,取反后就全部变成0了,于是while结束。    //限制:仅用于输入数字(字母、符号容易形成死循环)。
    while(scanf("%d",&a) == 1 && n)    //保证输入不为空且不是0。
    while(scanf("%d",&s) == 1) 按下Ctrl+z +回车 即可结束输入。但while(scanf("%d",&s) != '\n')或while(scanf("%d",&s) != EOF)是不可以。。。未解决。
  文件读取
   
      freopen("input.txt","r",stdin);
      freopen("output.txt","w",stdout);
    文件读取(不使用重定向)(较灵活)
      FILE fin,fout;
      fin = fopen("data.in","rb");
      fout = fopen("data.out","wb");

//代码效果参考:http://www.zidongmutanji.com/bxxx/577522.html
    不同:freopen不是指针类型的,写起来简单,自然,但不可同时读写文件和输入输出。
       fopen则需使用文件指针,且在使用fopen时,应使用配套的fsacnf和fprintf进行输入输出,普通的scanf和printf依然能在命令行界面操作。
       如果在fopen下想使用标准输入输出,只需赋值“fin = stdin;fout = stdout”即可,此时不在调用fopen和fclose。
  浮点数溢出
    在除法中(1/n^2)此时,需注意溢出,此时要使用连除(1/n/n)而不是直接除以其平方和(1/(nn))。
    第三章 数组和字符串
  数组声明:
    数组在程序中的声明位置很重要:参考:https://zhidao.baidu.com/question/397529275.html。
    全局变量(在main函数外声明)在静态存储区分配内存,局部变量(main函数内声明)是在栈上分配内存空间的,
    VC堆栈默认是1M,int a[1000000]的大小是4
1000000,将近4M,远远大于1M,编译连接的时候不会有问题,但运行是堆栈溢出,程序异常终止。
  内存复制:#include
    void memcpy(void dest, const void src, size_t n);
    函数说明:从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中
    常用方法:把数组a全部复制到数组b中:memcpy(b,a,sizeof(a));
  数组置零:#include
    void
memset(void s, int ch, size_t n);
    函数说明:将s中当前位置后面的n个字节 (typedef unsigned int size_t )用 ch 替换并返回 s。
    常用方法:将数组a清零:memset(a,0,sizeof(a));
  开关问题
    一般使用j % i 来判断几倍关系,用 !来取反模拟实现开启、关闭。
  蛇形填数(1)
    eg:10 11 12 1
       9 16 13 2
       8 15 14 3
       7 6 5 4
    类似上述问题使用数组,模拟笔撞墙(是否越界、是否填过)的问题。
    检验是否填过以及碰壁:(碰壁条件 && !a[x][y])而不是判断(a[x][y] == 0)。
  scanf注意事项与gets (#include )区别:  参考:https://zhidao.baidu.com/question/290403568.html.
    1、 gets功能为读入一行,并将换行符(\n)转换为字符串结束符(\0)。
    2、 scanf("%s",s);读入时,遇到空白字符,包括空格,制表符,换行符时均会停止输入。
      对于字符串数组scanf("%s",s)和scanf("%s",&s)是没有区别的,s代表数组首地址,&s也是一样。
      对于字符(int型或其他)来说参考以下:
        scanf函数要求第二个参数是一个指针类型的变量
        scanf("%d",a)----->表示a的定义为: int
a;
        scanf("%d",&a)---->表示a的定义为: int a;
      scanf("%d",s)和scanf("%s",&s)
    3、终止条件不同。
      gets只有遇到\n时才会结束输入,而scanf遇到空格或制表符时,也会结束输入。
      比如输入"test string\n"。
      用gets得到的字符串为"test string", 二用scanf得到的是"test"。
    4、终止后,对终止字符处理不同。
      比如输入为"test\nabcd"。
      执行gets后,\n不会留在缓冲区中,即这时调用getchar得到的字符是'a'。
      执行scanf后,\n会留在缓冲区,这时调用getchar得到的字符是'\n'。
  sprintf (#include )参考:https://baike.baidu.com/item/sprintf/9703430?fr=aladdin。应保证写入的字符串有足够的空间。
    作用:把格式化的数据写入某个字符串缓冲区。
    int sprintf( char buffer, const char format, [ argument] … );
    返回值:返回写入buffer 的字符数(\n计算在内,\0不计算在内),出错则返回-1. 如果 buffer 或 format 是空指针,且不出错而继续,函数将返回-1,并且 errno 会被设置为 EINVAL(不懂)。
    用例:
      char buffer[200], s[] = "computer";
      int j;
      j = sprintf( buffer, "%s\n", s );   //此时buffer为“computer”,j为9(8+1)。
  sizeof和strlen (#include )的区别:参考:https://www.cnblogs.com/carekee/articles/1630789.html
    sizeof为运算符其值在编译时即计算好了,参数可以是数组、指针、类型、对象、函数等。
    strlen(...)是函数,要在运行时才能计算。参数必须是字符型指针(char)。当数组名作为参数传入时,实际上数组就退化成指针了。
    
  strchr:(#include ) 参考:https://baike.baidu.com/item/strchr/10985184?fr=aladdin
    char
strchr(const char _Str,char _Val)
    功能:查找字符串_Str中首次出现字符_Val的位置。
    说明:返回首次出现_Val的位置的指针,返回的地址是被查找字符串指针开始的第一个与Val相同字符的指针,如果Str中不存在Val则返回NULL。
  fgets (#include ) 参考:https://baike.baidu.com/item/fgets/10942211?fr=aladdin。
    char
fgets(char buf, int bufsize, FILE stream);  
    参数:
      buf: 字符型指针,指向用来存储所得数据的地址。
      bufsize: 整型数据,指明存储数据的大小。       
stream: 文件结构体指针,将要读取的文件流。
    返回值:
成功,则返回第一个参数buf;
在读字符时遇到end-of-file,则eof指示器被设置,如果还没读入任何字符就遇到这种情况,则buf保持原来的内容,返回NULL;
如果发生读入错误,error指示器被设置,返回NULL,buf的值可能被改变。
    注意:从文件结构体指针stream中读取数据,每次读取一行。读取的数据保存在buf指向的字符数组中,每次最多读取bufsize-1个字符(第bufsize个字符赋'\0'),如果文件中的该行,不足bufsize-1个字符,则读完该行就结束。如若该行(包括最后一个换行符)的字符数超过bufsize-1,则fgets只返回一个不完整的行,但是,缓冲区总是以NULL字符结尾,对fgets的下一次调用会继续读该行。函数成功将返回buf,失败或读到文件结尾返回NULL。因此我们不能直接通过fgets的返回值来判断函数是否是出错而终止的,应该借助feof函数或者ferror函数来判断。
  fgetc (#include ) 参考:https://baike.baidu.com/item/fgetc/10942198?fr=aladdin。
    int fgetc(FILE stream);
    功 能:从流中取字符。
    fgetc()从参数stream所指的文件中读取一个字符,并把它作为一个字符返回。若读到文件尾或出现错误时,它就返回EOF(-1),你必须通过ferror或feof来区分这两种情况。
  getc (#include )参考:https://baike.baidu.com/item/getc/919590?fr=aladdin。
    int getc(FILE
stream);
    返回值:从文件指针stream指向的文件流中读取一个字符,并把它作为函数值返回给整型变量ch,并把位置标识符往前移动。
        如果读取失败或者到了文件结束标志返回EOF(-1)
  getchar (#include ) 参考:https://www.cnblogs.com/try-again/p/4975802.html。
    int getchar(void);
    参数:该函数没有参数。
    返回值:函数的返回值为用户输入的第一个字符的ASCII码,若出错返回-1,且将用户输入的字符回显到屏幕。如果用户在按回车键之前输入了不只一个字符,其他字符会保留在键盘缓冲区中,         等待后续getchar()调用读取。也就是说,后续的getchat()调用不会等待用户按键,而是直接读取缓冲区中的字符,直到缓冲区的字符读取完毕后,才等待用户按键。
  键盘输入错位问题(字符串常量数组)UVa10082 WERTYU
    方法一:(原著)使用的是先使用for( i = 1; s[i] && s[i] != c;i++);来获取当前输入字符位置,然后减一就是正确位置。
    方法二:使用strchr获取字符串位置。详见https://www.cnblogs.com/BANLOONG/p/10389951.html。
  isalpha (#include ) 参考: https://baike.baidu.com/item/isalpha/387277?fr=aladdin。
    int isalpha( int ch )
    功能:断字符ch是否为英文字母
    返回值:若为英文字母,返回非0(小写字母为2,大写字母为1)。若不是字母,返回0。
  isdigit (#include ) 参考:https://baike.baidu.com/item/isdigit/9455880?fr=aladdin。
    功能:用于检查其参数是否为十进制数字字符。
    返回值:若参数c为阿拉伯数字0~9,则返回非0值,否则返回0。
  isprint(#include )参考:http://c.biancheng.net/cpp/html/119.html。
    功能:判断字符c是否为可打印字符(含空格)。
    返回值:当c为可打印字符(0x20-0x7e)时,返回非零值,否则返回零。
    注意:判断字符c是否为可打印字符(含空格)说明:当c为可打印字符(0x20-0x7e)(ACSII中对应' ' 到 '~')时,返回非零值,否则返回零。0x7f(DEl)为控制字符,用iscntrl函数判断。
  iscntrl (#include ) 参考:http://c.biancheng.net/cpp/html/115.html。
    功能:用来判断字符是否为ASCII码的控制字符。
    返回值:若参数c 为ASCII 控制码,则返回非 0 值,否则返回 0。
    注意:控制字符的ASCII码值 0x00 ~ 0x1F,再加上 0x7f(DEL),控制字符在屏幕上显示时不占位(看不见);
  注:将字符转换为数字、字母的函数。
    #include
    char r(char ch){
      if(isalpha(ch)) return ch - 'A';
      return ch-'0'+25;
  回文(aba)+镜像(5↔Z)问题 :UVa401 Palindromes
    用两个标志位分别代替是否回文和是否镜像,共四种结果,此时可以使用数组来保存这四种结果,输出时由两个标志位计算即可得出结果
    eg:char msg[] = {"非回文","回文","镜像","镜像+回文"}
      p = 0;  //不是回文    m = 0;  //不是镜像
      printf("%s",msg[m
2+p]);
    注意:字符数组顺序需注意匹配结果。
  猜数字问题:UVa340 Master-Mind Hints 参考:https://blog.csdn.net/weixin_39274830/article/details/79179057。
    给定答案序列和用户猜的序列,统计有多少数字位置正确(A),有多少数字在两个序列都出现过但位置不对(B)。
    //直接统计得A,为了求B,对于每个数字(1—9),统计二者出现的次数c1和c2,则min(c1,c2)就是该数字对B的贡献,最后要减去A的部分。
    同一数字c1和c2都存在(一次都没有出现记为0),证明该数字在两个序列中皆出现过,但此时的出现包括两种情况(位置正确的(即A)和不正确的(即B)),而如下例eg1中的d=3,c1=1,c2=0,说明该数字在b[i]中没有出现,当然不计入A或B中,同理,eg2中的d=6,c1=0,c2=2,说明该数字在a[i]中不存在,同样不计入A或B。
    eg1:a[i]  1 3 5 5 5
       b[i]  5 5 2 6 6
    eg2: a[i]  1 3 2 5 5
       b[i]  5 5 5 6 6
    在d=5时,eg1中c1为3,c2为2,c1>c2,在最好的情况下,b[i]中的2个5位置都正确,
         eg2中,c1为2,c2为3,c1<c2.在最好的情况下,也只有2个5满足条件,因此总的来说,就是取较小值。(自己掰的)。
复制代码//代码效果参考:http://www.zidongmutanji.com/zsjx/59557.html

1 for(;;){
2 int A=0,B=0;
3 用for循环直接比较得出A;  //伪代码
4 结束输出;          //伪代码
5 for(int d=1;d<=9;d++){    //统计数字d(1~9)在答案序列a[i]和猜测序列b[i]中各出现次数。
6 int c1=0,c2=0;
7 for(int i=0;i b){
3 if (a % b == 0)return b;
4 else{
5 a -=b;
6 max_n(a,b);
7 }
8 } else{
9 int temp = a;
10 a = b;
11 b = temp;
12 max_n(a,b);
13 }
14 }
  最小公倍数为二数之积除以最大公约数。

相关文章
|
24天前
|
存储 算法 Linux
C语言 多进程编程(一)进程创建
本文详细介绍了Linux系统中的进程管理。首先,文章解释了进程的概念及其特点,强调了进程作为操作系统中独立可调度实体的重要性。文章还深入讲解了Linux下的进程管理,包括如何获取进程ID、进程地址空间、虚拟地址与物理地址的区别,以及进程状态管理和优先级设置等内容。此外,还介绍了常用进程管理命令如`ps`、`top`、`pstree`和`kill`的使用方法。最后,文章讨论了进程的创建、退出和等待机制,并展示了如何通过`fork()`、`exec`家族函数以及`wait()`和`waitpid()`函数来管理和控制进程。此外,还介绍了守护进程的创建方法。
C语言 多进程编程(一)进程创建
|
24天前
|
Linux C语言
C语言 多进程编程(三)信号处理方式和自定义处理函数
本文详细介绍了Linux系统中进程间通信的关键机制——信号。首先解释了信号作为一种异步通知机制的特点及其主要来源,接着列举了常见的信号类型及其定义。文章进一步探讨了信号的处理流程和Linux中处理信号的方式,包括忽略信号、捕捉信号以及执行默认操作。此外,通过具体示例演示了如何创建子进程并通过信号进行控制。最后,讲解了如何通过`signal`函数自定义信号处理函数,并提供了完整的示例代码,展示了父子进程之间通过信号进行通信的过程。
|
24天前
|
Linux C语言
C语言 多进程编程(四)定时器信号和子进程退出信号
本文详细介绍了Linux系统中的定时器信号及其相关函数。首先,文章解释了`SIGALRM`信号的作用及应用场景,包括计时器、超时重试和定时任务等。接着介绍了`alarm()`函数,展示了如何设置定时器以及其局限性。随后探讨了`setitimer()`函数,比较了它与`alarm()`的不同之处,包括定时器类型、精度和支持的定时器数量等方面。最后,文章讲解了子进程退出时如何利用`SIGCHLD`信号,提供了示例代码展示如何处理子进程退出信号,避免僵尸进程问题。
|
24天前
|
消息中间件 Unix Linux
C语言 多进程编程(五)消息队列
本文介绍了Linux系统中多进程通信之消息队列的使用方法。首先通过`ftok()`函数生成消息队列的唯一ID,然后使用`msgget()`创建消息队列,并通过`msgctl()`进行操作,如删除队列。接着,通过`msgsnd()`函数发送消息到消息队列,使用`msgrcv()`函数从队列中接收消息。文章提供了详细的函数原型、参数说明及示例代码,帮助读者理解和应用消息队列进行进程间通信。
|
24天前
|
缓存 Linux C语言
C语言 多进程编程(六)共享内存
本文介绍了Linux系统下的多进程通信机制——共享内存的使用方法。首先详细讲解了如何通过`shmget()`函数创建共享内存,并提供了示例代码。接着介绍了如何利用`shmctl()`函数删除共享内存。随后,文章解释了共享内存映射的概念及其实现方法,包括使用`shmat()`函数进行映射以及使用`shmdt()`函数解除映射,并给出了相应的示例代码。最后,展示了如何在共享内存中读写数据的具体操作流程。
|
24天前
|
消息中间件 Unix Linux
C语言 多进程编程(二)管道
本文详细介绍了Linux下的进程间通信(IPC),重点讨论了管道通信机制。首先,文章概述了进程间通信的基本概念及重要性,并列举了几种常见的IPC方式。接着深入探讨了管道通信,包括无名管道(匿名管道)和有名管道(命名管道)。无名管道主要用于父子进程间的单向通信,有名管道则可用于任意进程间的通信。文中提供了丰富的示例代码,展示了如何使用`pipe()`和`mkfifo()`函数创建管道,并通过实例演示了如何利用管道进行进程间的消息传递。此外,还分析了管道的特点、优缺点以及如何通过`errno`判断管道是否存在,帮助读者更好地理解和应用管道通信技术。
|
24天前
|
存储 Ubuntu Linux
C语言 多线程编程(1) 初识线程和条件变量
本文档详细介绍了多线程的概念、相关命令及线程的操作方法。首先解释了线程的定义及其与进程的关系,接着对比了线程与进程的区别。随后介绍了如何在 Linux 系统中使用 `pidstat`、`top` 和 `ps` 命令查看线程信息。文档还探讨了多进程和多线程模式各自的优缺点及适用场景,并详细讲解了如何使用 POSIX 线程库创建、退出、等待和取消线程。此外,还介绍了线程分离的概念和方法,并提供了多个示例代码帮助理解。最后,深入探讨了线程间的通讯机制、互斥锁和条件变量的使用,通过具体示例展示了如何实现生产者与消费者的同步模型。
|
24天前
|
Linux C语言
C语言 多进程编程(七)信号量
本文档详细介绍了进程间通信中的信号量机制。首先解释了资源竞争、临界资源和临界区的概念,并重点阐述了信号量如何解决这些问题。信号量作为一种协调共享资源访问的机制,包括互斥和同步两方面。文档还详细描述了无名信号量的初始化、等待、释放及销毁等操作,并提供了相应的 C 语言示例代码。此外,还介绍了如何创建信号量集合、初始化信号量以及信号量的操作方法。最后,通过实际示例展示了信号量在进程互斥和同步中的应用,包括如何使用信号量避免资源竞争,并实现了父子进程间的同步输出。附带的 `sem.h` 和 `sem.c` 文件提供了信号量操作的具体实现。
|
1月前
|
存储 程序员 C语言
揭秘C语言:这些核心知识你掌握了吗?一篇文章带你突破编程基础,开启高效编码之旅!
【8月更文挑战第22天】C语言作为编程基石,以简洁高效著称,历经数十年仍备受欢迎。本文通过梳理C语言的核心概念,帮助读者深入理解并提升技能。适合各水平读者。基础语法从`main`函数开始,如示例中的“Hello, World!”程序所示。C语言强调头文件包含与语句结尾的分号。变量和数据类型丰富多样,如`int`、`float`、`char`等,合理选择可优化内存使用和性能。指针用于间接访问内存,是C语言的关键特性。控制结构如循环和分支使程序逻辑更灵活。函数支持代码复用与模块化。深入学习还需掌握预处理指令、文件操作等高级特性。通过系统学习与实践,你将能更熟练地使用C语言,构建高效稳定的应用。
38 4
|
1月前
|
存储 编译器 数据处理
【编程秘籍】解锁C语言数组的奥秘:从零开始,深入浅出,带你领略数组的魅力与实战技巧!
【8月更文挑战第22天】数组是C语言中存储同类型元素的基本结构。本文从定义出发,详述数组声明、初始化与访问。示例展示如何声明如`int numbers[5];`的数组,并通过下标访问元素。初始化可在声明时进行,如`int numbers[] = {1,2,3,4,5};`,编译器自动计算大小。初始化时未指定的元素默认为0。通过循环可遍历数组,数组名视为指向首元素的指针,方便传递给函数。多维数组表示矩阵,如`int matrix[3][4];`。动态数组利用`malloc()`分配内存,需用`free()`释放以避免内存泄漏。掌握这些技巧是高效数据处理的基础。
55 2