C语言

  • GCC下“initializing ‘char **’ with an expression of type ‘const char **’ discards qualifiers in nested pointer types”警告解读

    ·

    我曾经被这个警告困扰了好久好久,问了好些人,都说没遇到过=_=… 问题代码片段(与问题无关的代码块已被忽略): size_t MyConv(const char *orginStr, const char *targetStr, …) { char **tmpIn = NULL; char **tmpOut = NULL; … … tmpIn = &orginStr; tmpOut = &targetStr; … … } GCC编译后会弹出如下警报: sfind.c:887:8: warning: assigning to ‘char **’ from ‘const char **’ discards qualifiers in nested pointer types [-Wincompatible-pointer-types-discards-qualifiers] tmpIn = &orginStr; ^ ~~~~~~~~~ 这条警告信息翻译出来是“用类型表达式初始化“char \**”…

    Read More

  • 在Linux下利用宏定义的方式使用Sleep函数

    ·

    前言 Sleep是存在于Windows.h头文件中用于将进程挂起的函数。显然,Linux下是没有该头文件的,该如何实现呢? 实现方法 Linux的unistd.h库为我们提供了类似功能的usleep函数,但该函数的计时单位是微妙而不是和Sleep一样使用毫秒计时。 为了方便程序的跨平台开发,可以使用如下宏定义将usleep化妆为Sleep。 #ifdef linux #include<unistd.h> #define Sleep(value) usleep(value * 1000); #endif 只需将以上宏定义加到源码中就可以正常的在Linux下使用Sleep函数了。

    Read More

  • GNU出品的转码库——libiconv v1.15 for windows x64下载(已经过编译)

    ·

    libiconv库是GNU项目的一部分,负责对字符进行不同编码间的转换。是Linux下非常常用的编码转换库,其对应的命令是:iconv(命令的使用方法可以百度了解下)。 libiconv库原生支持Windows平台,但考虑到很多人为了追求效率而不想自己动手编译这个庞大而复杂的库,故我在这里为大家提供已经编译好的支持windows 64位的二进制文件——可以动态调用也可以静态编入程序,提供Debug和Release两个版本。注:使用本库请确保你的项目是64位的,否则无法正常调用。 二进制文件已在Win10 + VS 2017环境中测试通过。提供的版本为libiconv v1.15,该版本发布于2017年2月,截至本文章发布时为libiconv库的最新版本。 下载地址:libiconv-1.15 for windows x64

    Read More

  • C语言swprintf函数的正确用法

    ·

    在这篇文章的开头,我要狠狠的吐槽国内那些专门抄别人文章的网站主们,抄是可以抄,但是一篇错的文章还抄来抄去的有意思吗??? swprintf函数原型: int swprintf(wchar_t * ws,size_t len,const wchar_t * format,…); 参数说明: ws 指向存储宽字符串的缓冲区的指针。缓冲区的大小至少为n个宽字符。 len 填充ws缓冲区的最大宽字符数。生成的字符串长度至多为n-1,为额外的’0’留一个空间。 format 宽字符串,其中包含格式字符串,其格式与printf中的格式相同。请注意,所有格式说明符的含义与printf中的相同; 因此,应使用%lc写宽字符(而不是%c),并且%ls应用于宽字符串(而不是%s)。 … (附加参数) 同printf的附加参数,不做过多解释。 程序示例: #include <stdio.h> #include <wchar.h> int main (void) { wchar_t str [100] = L""; swprintf (str, 100, L"%ls%d is %d",L"The half of " ,80, 80/2); fputws (str, stdout); return 0; }

    Read More

  • C语言操作文件时写完一组数据后立即刷新缓存区的方法

    ·

    前言 很多时候,为了防止程序意外结束等原因造成的数据丢失,我们需要在向文件写入一组数据以后立即刷新缓存区。比如说:在写程序LOG的时候,为了防止程序崩溃时无法记录LOG,就需要记录一条LOG就刷新一次缓存区以使其立即写入磁盘文件。 实现方法 使用C标准库提供的fflush(FILE *)函数可以很方便的实现该功能,只需传入要刷新缓存区的文件指针即可。 示例程序 #include <stdio.h> int main(void) { FILE *fp = fopen(“LOG.txt”, “w”); fprintf(fp, “hello!”);// 使用一个明显小于缓存区的字符串做测试,若大于缓存区则缓存区在满了以后会自动刷新 fflush(fp); // 立即刷新缓存区,若删除此语句则字符串不会被保存在文件中。 fp++; // 偏移文件指针到未知地址。 fclose(fp); // 由于fp指向未知地址,所以执行fclose后程序会崩溃。 return 0; } 经观察可以发现,不使用fflush()函数,字符串就不能被正确的保存在文件中。

    Read More

  • 原来C语言的数组大小可以是一个变量呀!

    ·

    长久以来,我一直以为C语言的数组大小必须是在编译前就确定下来的常量值,相信很多C语言初学者也是和我一样的认识。然而,直到今天我才发现原来从C99标准开始C语言就已经支持了可变长数组。 例如如下程序(注:VS系列编译器均不支持该特性,搞不懂微软为何不好好支持C99标准): #include <stdio.h> int main(void) { int a = 2; int b[a + 1]; // 数组b的长度只有在运行时才可以确定。 for (int i = 0; i < 3; i++) { b[i] = i; printf("%d\n", b[i]); } return 0; } 上面的代码在GCC 7.3上测试通过(实际上任何支持C99标准的编译器都可以)。需要注意的是,在C语言中,可变长数组只能用于局部变量中,在全局变量中使用可变长数组会导致编译器报错。另外,可变长数组不等于动态数组,也就是说,数组的维度在变量的整个生命周期中是不可变的。可变长数组对于二维、三维…的数组及类似int (*a)[b/*b是个变量*/ + 2];这种指向二维数组的指针也一样适用。

    Read More

  • C语言实现Linux下遍历指定目录批量搜索及替换文件中关键字的小工具

    ·

    为什么要写这样一个程序呢? 兼职做IDC的我经常会需要帮客户改网站,很多时候会需要将用户网站中所有脚本文件中的某一关键字进行替换(可能是品牌名称、网址等),在Windwos下一般都是用Dreamweaver来实现的,但总有一种杀鸡用牛刀的感觉。后来电脑重装为Linux平台后一直没发现好的批量替换工具,这导致很长一段时间以来我一直是用grep命令先搜索关键字,再挨个文件手工替换=_=。。。 寒假前听说高考科那边要开C语言的课程了,索性就跟着学啦,因为我初中的时候有看过一点点C++的知识,所以C学起来也还算轻松,到放寒假时已经学了两个月了,感觉是时候利用假期来写点什么了。想到前面说的痛点,我就决定写一个小巧的工具,可以方便的批量替换目录中所有文件中包含的关键字。 程序简介 首先声明,这是我写的第一个程序(打印hello, world那种“程序”不算哈),水平有限,若是代码中什么可笑的地方还望大佬们可以给指出来^_^。 程序是用C语言+Shell脚本实现的,目前仅支持Linux平台,计划支持Windows(工作量不会很大,核心代码通用)。 程序中关键字的数据存储是使用链表这种数据结构实现的,没学过链表的同学看起来可能比较吃力。 若是程序中有什么BUG或是想和我交流一下可以在文章下方留言。 程序源码: GitHub:https://github.com/sunxiyuan/sfind 感觉还可以的话给个Star呗~

    Read More

  • C语言实现删除一个字符串中的指定字母

    ·

    题目要求 删除一个字符串中的指定字母,如:字符串 “hello,world!”,删除其中的 o 字母,应输出 “hell,wrld!” 解题思路 将字符串内不属于需搜索的字符的字符再赋值到该字符串的地址空间中,最后再在字符串结尾加上结束标记\0。 程序代码 #include <stdio.h> #include <string.h> int main(int argc, char **argv) { int num = 0; for (int i = 0; i <= strlen(argv[1]); i++) if (argv[1][i] != argv[2][0]) { argv[1][num] = (i == strlen(argv[1])) ? ‘\0’ : argv[1][i]; num++; } printf(“%s\n”, argv[1]); return 0; } 运行结果

    Read More

  • C语言依据用户输入的数据大小动态开辟内存空间

    ·

    前言 使用C语言编程时经常需要从各种途径读取数据并将其保存在内存中,但C语言为了保持简洁,所以只包含一些很基础的数据类型,有时候并不能很好的存储用户输入的数据。 例如:需要存储用户输入的字符串,通常,教科书上所教的方法是分配一个较大的数组来进行存储。显然,这在实际生产环境中是相当不切实际的。 为了相对完美的解决这个问题,我们可以通过动态的开辟堆空间来实现对数据的存储。 例程简介 此例程每次接收一个字符,并利用realloc函数根据数据量大小来重新分配堆空间。 例程代码 #include <stdio.h> #include <stdlib.h> #include <stdbool.h> int main(void) { char tmp = ‘\0’; char *str = NULL; for (int i = 0; true; i++) { tmp = getchar(); if ((tmp == ‘\r’) || (tmp == ‘\n’)) break; /*每次循环都按照当前数据量大小来重新分配堆空间大小*/ str = (char *) realloc(str, sizeof(str) + sizeof(tmp)); if (str…

    Read More

  • 解决Ubuntu下无法使用pthread库的问题(加-lpthread也不好使的情况)

    ·

    导语 这个问题困扰了我很久,因为写一个飞机大战的小游戏需要用到多线程。看到Linux下的多线程可以用pthread.h库来实现,但该库不是C的标准库,所以编译的时候要加上-lpthread,可是,为在我的Ubuntu 16.04下如此编译的结果是报了这个错误: /usr/bin/ld: 找不到 -lphtread collect2: error: ld returned 1 exit status 为了解决这个问题,我在网上苦苦地搜了很久很久,最后无奈地发现:网上现有的文章基本上都是抄来抄去,全是胡言乱语。。。 正确的解决办法 安装manpages-posix软件包即可。 $ sudo apt install manpages-posix 再次编译即可通过!

    Read More

  • 我对C语言中字符串指针的一个误解

    ·

    在QQ群中看到一道C语言题: 定义char a[10]和char *p=a,下面的赋值语句中,正确的是___ A. a[10]=”Turbo C” B. p=”Turbo C” C. a=”Turbo C” D. *p=”Turbo C” 这道题选B本身是没有任何值得疑惑的地方的,但是,且看我下面这个小程序。 #include<stdio.h> int main(void) { char a[10]; char *p = a; p = "Turbo C"; printf("%s\n", a); //这里我打印的是数组a而不是指针p return 0; } 在Linux下编译执行后输出乱码,这个错误是因为我一开始以为:p = “Turbo C”是将”Turbo C”这个字符串赋值给了p所指向的字符数组a。实际上,p = “Turbo C”这条语句在执行时做了三件事。 在内存中创建了一个常量空间,用于存放字符串 在字符串末尾添加了一个/0 返回字符串在内存中存放位置的首地址给p指针 所以,p指针中所保存的内容已经由字符数组a的首地址变为了字符串”Turbo C”的首地址,想当然的,字符串”Turbo C”也并未被赋值给字符数组a,而是存在于新的内存空间中。此时我再用printf输出字符数组a的内容就会打印出一些乱七八糟的东西啦——因为我没有给数组a初始化,只有天才晓得编译器在里面填了什么东东~ 那么上面那道程序这样改一下就正常工作啦:将第7行改为 printf("%s\n", p);

    Read More

  • C11新增多线程支持库-threads.h参考手册

    ·

    线程管理 int thrd_create(thrd_t *thr, thrd_start_t func, void *arg); thrd_create创建一个新线程,该线程的工作就是执行func(arg)调用,程序员需要为线程编写一个函数,函数签名为:thrd_start_t ,即int (*)(void*)类型的函数。新创建的线程的标识符存放在thr内。 thrd_t thrd_current(void); thrd_current函数返回调用线程的标识符。 int thrd_detach(thrd_t thr); thrd_detach会通知操作系统,当该线程结束时由操作系统负责回收该线程所占用的资源。 int thrd_equal(thrd_t thr0, thrd_t thr1); thrd_equal用于判断两个线程标识符是否相等(即标识同一线程),thrd_t是标准约定的类型,可能是一个基础类型,也可能会是结构体,开发人员应该使用thrd_equal来判断两者是否相等,不能直接使用==。即便==在某个平台下表现出来是正确的,但它不是标准的做法,也不可跨平台。 void thrd_exit(int res) thrd_exit函数提早结束当前线程,res为它的退出状态码。这与进程中的exit函数类似。 int thrd_join(thrd_t thr, int *res) thrd_join将阻塞当前线程,直到线程thr结束时才返回。如果res非空,那么res将保存thr线程的结束状态码。如果某一线程内没有调用thrd_detach函数将自己设置为detach状态,那么当它结束时必须由另外一个线程调用thrd_join函数将它留下的僵死状态变为结束,并回收它所占用的系统资源。 void thrd_sleep(const xtime *xt) thrd_sleep函数让当前线程中途休眠,直到由xt指定的时间过去后才醒过来。 void thrd_yield(void) thrd_yield函数让出CPU给其它线程或进程。 互斥对象和函数 threads.h中提供了丰富的互斥对象,用户只需在mtx_init初始化时,指定该互斥对象的类型即可。 int mtx_int(mtx_t *mtx, int type); mtx_init函数用于初始化互斥对象,type决定互斥对象的类型,一共有下面6种类型: mtx_plain –简单的,非递归互斥对象 mtx_timed –非递归的,支持超时的互斥对象 mtx_try…

    Read More

  • 使用C11新增的多线程支持库-threads.h进行多线程编程

    ·

    2019年12月6日更新 首先要感谢评论区的热心的同学们的提醒,经本人亲自验证: VS 2019中移除了对threads.h的支持(评论区有同学提到单独安装v140工具集可以实现支持,但是测试发现没有效果),而GCC方面则在最新版中加入了对该头文件的支持。 以下为原文(程序代码部分更新了Linux版Dome)。 导语 threads.h是C11标准新增的多线程支持库,在此之前C语言实现多线程,除了使用系统API外用的最多的就是pthread.h了,threads.h在语法上和pthread.h非常相似。 当然,对于新出的C语言标准,各大编译器厂商并不会马上就支持。就比如说,Linux下主流的C语言编译器————GCC,直到GCC7.2版本都没能支持该库(PS:看到老外网站上说要安装最新版glibc才能获得对该库的支持,然而亲测无卵用)。相反,VS在这方面做的就很不错,VS2017已经可以完美支持该库了,本文也将基于VS2017社区版对该库的使用方法做介绍。 注:本文仅对多线程编程的概念及threads.h库文件的使用方法做简单介绍,并不会详尽介绍该库下的所有函数,如果你需要一个函数功能的参考手册可以参考此篇文章:C11新增多线程支持库-threads.h参考手册 以一个小程序为例子 本程序中使用到的库函数及宏: thrd_t //此宏定义用于存放线程标识符的数据类型 thrd_create //此函数用于创建线程 thrd_detach //此函数用于通知操作系统,当线程结束时由操作系统负责释放资源 thrd_exit //此函数用于结束当前线程 程序功能: 主线程每2秒打印一次“I love ibadboy.net~~~”,共打印10次。子线程每1秒打印一次“He love ibadboy.net!!!”,共打印10次。我们知道,在只有一个主线程的C程序中该功能是无法实现的,因为后一段程序代码必须等待前一段代码执行完毕才可执行。但,在多线程编程中,各个线程可以一起执行(这里涉及到的同步、异步等等的高阶技术就不讨论了)。举个例子:在游戏开发中,如果程序需要实时监控用户键盘的输入,就不能把这段代码放到主线程中,因为这样的话该段代码就会被程序的其他部分阻塞掉而无法做到真正的“实时”,这时就可以利用多线程技术来化解尴尬啦!话不多说,直接上代码! 程序代码Linux版本 #include <stdio.h> #include <unistd.h> //包含sleep等函数 #include <stdbool.h> #include <threads.h> //包含多线程支持库头文件 #include <stdlib.h> //包含exit等函数 int thr_fun(void *); int main(void) { thrd_t thr; int ret; //保存thrd_create函数的返回值用于判断线程是否创建成功:0为成功,1为失败。 ret = thrd_create(&thr, thr_fun, NULL);…

    Read More