Who are you?

C海拾贝

​ 这是一篇杂文,主要用于记录一些平常阅读C/C++代码coding时看到的比较OK的一些用法,慢慢积累,还是不错的~

20170215-代码风格基本规范

  1. 采用缩进对代码进行分级

    • 建议Fortran代码采用2个空格进行缩进
    • C 代码采用4个空格进行缩进
  2. 适当使用空格进行分段

  3. 对功能段为单位对代码进行注释,使代码流程一目了然

  4. 函数/子程序前面添加一个注释块,一是分隔代码;而是添加相关说明,包括:函数目的,开发者信息,日期,版本变动历史等

    1
    2
    3
    4
    5
    !-------------------------------------------------
    ! Purpose: initialize the global variables
    ! Author : SAN ZHANG
    ! Date : 2016/09/19
    !-------------------------------------------------
  5. Fortran 编程规范

    • do/end do, if/end if 如果中间包括的语句较多,超过一屏显示,建议在end do/end if 后面添加注释对哪个变量循环:

      1
      2
      do iblk = 1, nblk
      end do !-- loop of iblk
    • enddo / end do 两种写法,建议采用后一种;

    • if 同后面的括弧之间有一个空格

    • 有些规范要求do/if等关键词大写,我们目前采用小写规范;待进一步考察;

    • 在代码的最后添加一行

      1
      ! vim:ft=fortran:ts=4:sw=4:nu:et:ai:

      以让VIM自动实现缩进、将tab替换成空格等

  6. 编写程序时的基本规范

    • 读入参数需要回显到屏幕,以用于记录运行参数和调试;
    • 数量较多的输入,如格点速度文件,可以显示最大、最小值等;
    • 通过 _DEBUG 预定义宏进行调试相关的输出,以防止降低正常运行时的速度

20170215-自定义程序输入或输出文件名

以前在自己编程序实践中,很多时候都想在程序运行时通过交互的方式让使用者自行定义某些输入或是输出文件的文件名,但是一直找不到合适的方法实现,但是现在可以通过以下代码实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include<iostream>
#include<fstream>
#include<string>
int main()
{
using namespace std;
string filename;
cout<<"Enter the name of the file"<<endl;
cin>>filename;
/* 使用c_str()方法指定输出文件名 */
ofstream fout(filename.c_str());
...
}

Tips

  1. 首先必须包含头文件string;
  2. 在使用时需要用到string类的c_str()方法;

参考:

  • C++ primer plus - 6 ,人民邮电出版社

20170215-const与指针

简便记法(1)

const后面的内容为常量:

1
2
3
4
const int p; // p 为常量,初始化后不可更改
const int* p; // *p 为常量,不能通过*p改变它指向的内容
int const* p; // *p 为常量,同上
int* const p; // p 为常量,初始化后不能再指向其它内容

简便记法(2)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const int p;
const int* p;
int const* p;
int * const p;
const int * const p;
int const * const p;
/* 将 p 替换为 p is a
* 将 * 替换为 point to
* 从右往左读 */

细说const

当使用带有const的指针时其实有两种意思。一种指的是你不能修改指针本身的内容(其实就是指向的地址),另一种指的是你不能修改指针指向的内容(其实就是该地址上的数据)。

  1. 指向const的指针,它的意思是指针指向的内容是不能被修改的。它有两种写法:

    1
    2
    const int* p; (推荐)
    int const* p;
    • 第一种可以理解为,p是一个指针,它指向的内容是const int 类型。p本身不用初始化它可以指向任何标示符,但它指向的内容是不能被改变的。
    • 第二种很容易被理解成是p是一个指向int的const指针(指针本身不能被修改),但这样理解是错误的,它也是表示的是指向const的指针(指针指向的内容是不能被修改的),它跟第一种表达的是一个意思。为了避免混淆推荐大家用第一种。
  2. const指针,它的意思是指针本身的值是不能被修改的。它只有一种写法:

    1
    int* const p=一个地址; //(因为指针本身的值是不能被修改的所以它必须被初始化)
    • 这种形式可以被理解为,p是一个指针,这个指针是指向int 的const指针。它指向的值是可以被改变的如*p=3;
  3. 还有一种情况是这个指针本身和它指向的内容都是不能被改变的:

    1
    2
    const int* const p=一个地址;
    int const* const p=一个地址;

Tip

一个规律就是: 指向const的指针(指针指向的内容不能被修改)const关健字总是出现在*的左边而const指针(指针本身不能被修改)const关健字总是出现在*的右边,那不用说两个const中间加个*肯定是指针本身和它指向的内容都是不能被改变的。

参考:


20170302-main函数参数

mian()函数参数

一般我们所写的主函数都是void main()或是int main(){return 0;},但是还有一种较为普遍的形式:

1
2
3
4
5
//first
int main(void)
//second
int main(int argc, char *argv[])
int main(int argc, char **argv)

其中argc,argv用于在程序运行时将参数传入主程序,其中arg就是argument,即参数。具体含义如下:

  1. int argc : argument count(参数计数)

    含义:count of cmd line args,运行程序传送给main函数的命令行参数总个数,注意其包含可执行程序名,因而当argc=1时表示有一个程序名称,其存储在argv[0]中

  2. char *argv[ ]:arguments value / vector(参数值)

    含义:pointer to table of cmd line args,字符串数组,用来存放指向字符串参数的指数指针,每个元素指向一个参数,空格分隔参数,其长度为argc,数组下标从0开始,argv[argc]=NULL

    • argv[0]:程序运行时的全路径名
    • argv[1]:指向程序在DOS命令中执行程序名后的第一个字符串
    • argv[2]:指向执行程序名后的第二个字符串
    • argv[argc]=NULL

在程序中我们可以直接调用这些参数。

参考:


20170303-程序运行计时

struct timeval

原型

1
2
3
4
5
struct timeval
{
__time_t tv_sec; /* Seconds. */
__suseconds_t tv_usec; /* Microseconds. */
};
  • tv_sec:Epoch到创建struct timeval时的秒数
  • tv_usec:Epoch到创建struct timeval时的微秒数,即秒后面的零头
  • Epoch:一个Unix时间戳,表示1970年1月1日00:00:00 UTC,由此得到的时间戳不考虑闰秒

gettimeofday()

原型

1
2
3
4
5
6
7
/* Get the current time of day and timezone information,
putting it into *TV and *TZ. If TZ is NULL, *TZ is not filled.
Returns 0 on success, -1 on errors.
NOTE: This form of timezone information is obsolete.
Use the functions and variables declared in <time.h> instead. */
extern int gettimeofday (struct timeval *__restrict __tv,
__timezone_ptr_t __tz) __THROW __nonnull ((1));
  • gettimeofday()功能是得到当前时间和时区,分别写到tv和tz中,如果tz为NULL则不向tz写入

example

  1. 直接获取当前的时间

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    #include <sys/time.h>
    #include <stdio.h>
    int
    main(void)
    {
    int i;
    struct timeval tv;
    for(i = 0; i < 4; i++){
    gettimeofday(&tv, NULL);
    printf("%d\t%d\n", tv.tv_usec, tv.tv_sec);
    sleep(1);
    }
    return 0;
    }
  2. 测试程序用时

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    //此部分内容用于计算time cost
    struct timeval startTime;
    struct timeval endTime; /* Start and end times */
    struct timeval costStart; /*only used for recording the cost*/
    double totalCost = 0;
    void cost_start()
    {
    gettimeofday(&costStart, NULL);
    }
    void cost_end()
    {
    double elapsed=0;
    struct timeval costEnd;
    gettimeofday(&costEnd, NULL);
    elapsed = ((costEnd.tv_sec*1000000+costEnd.tv_usec)- (costStart.tv_sec*1000000+costStart.tv_usec))/1000000.0;
    totalCost += elapsed;
    }

参考:


20170609-字符串连接

sprintf

1
sprintf(des, "%.*s %.*s %.*s", 3, "foo", 3, "bar", 3, "baz");
  • des:目标区域的字符串指针
  • %.*s:用于控制字符串的长度
  • 可以使用sprintf命令实现将数字转换为字符串。

strcat

  • 原型:char *strcat(char *dest, const char *src);

  • 参数:dest 为目的字符串指针,src 为源字符串指针。

  • 作用:strcat() 会将参数 src 字符串复制到参数 dest 所指的字符串尾部;dest 最后的结束字符 NULL 会被覆盖掉,并在连接后的字符串的尾部再增加一个 NULL。注意:dest 与 src 所指的内存空间不能重叠,且 dest 要有足够的空间来容纳要复制的字符串。

  • 实例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include <stdio.h>
    #include <string.h>
    int main ()
    {
    char str[80];
    strcpy (str,"these ");
    strcat (str,"strings ");
    strcat (str,"are ");
    strcat (str,"concatenated.");
    puts (str);
    return 0;
    }