跳转至内容

脉冲星和中子星/脉冲星天文学家 C 语言入门

来自维基教科书,开放的书籍,开放的世界

许多基于脉冲星的软件包是用 C 语言编写的(例如 tempo2 和 sigproc)。

C 程序首先被写成一个文本文件(或一组文本文件),使用一个文本编辑器(有一些专门的编辑器用于编写程序,但也可以使用 emacs 或 vi)。然后需要编译程序才能运行它们。任何对程序的更改都需要重新编译。

您可以使用以下命令检查您是否安装了 gcc 以及版本号

$ gcc -v

一个基本程序

[编辑 | 编辑源代码]
/*
Anything between the * symbols will be a comment
This can go over multiple lines.
*/

// You can also comment on a single line like this
#include <stdio.h> // This is a library linked to input and output. This should always be included at the top of the program
#include <math.h>  // This is a library related to math functions

// All C programs have to have a function called main.  
int main()
{  // Everything between this { and the } will be part of the main function
 printf("Hello everyone!\n");  // The \n is a new line.  The ; symbol states that this is the end of a command
}

将代码保存为 test.c。使用以下命令编译

$ gcc test.c

这将产生一个名为“a.out”的输出文件。要运行

$ ./a.out

您也可以使用

$ gcc -o test test.c

这将生成一个名为“test”的输出文件,可以使用以下命令运行

$ ./test

一些变量

[编辑 | 编辑源代码]
#include <stdio.h> 
#include <math.h>  


int main()
{
 int x; // Variables must (in C) be defined at the top of a function. Here we say that x is an integer.  Note that by default this is not
        // set to any particular value. It could be anything
 float y; // This is a floating point number (i.e., a number with a decimal point - e.g., 1.23455)
 float z;

 // We can set these values
 x = 5;
 y = 1.2345;
 z = y*2+x;  // Maths is quite straightforward.  You can use the +,-,*,/ symbols. Note that there's no default C command for "to the power".
  
 printf("Hello everyone!\n");  
 printf("x = %d, y = %f and z = %f\n",x,y,z); // %d means print an integer and %f is used to print a float
}

程序员可以创建他们自己的函数。这些函数可能是仅在当前正在编写的特定程序中使用的函数,也可能是在许多不同程序中更频繁地使用的函数。

函数可以简单地打包一些代码,这些代码可以多次运行。

#include <stdio.h>
#include <math.h>

// Declare the function that we're going to create
void writeHeaderInfo(); // The void states that this function will not return any results

int main()
{
  writeHeaderInfo(); // This is a simple function to print out some information about the program. A function is called by giving its name and the brackets.
}

// We can now create the function. The next line should be identical to the declaration of the function at the top of the code (apart from the semi-colon)
void writeHeaderInfo()
{
 printf("Hello World. This is coming from the writeHeaderInfo function\n");
}

请注意,函数不能访问函数外部的数据(除非您使用全局变量 - 不要使用)。因此,如果您在一个函数中定义一个值,它不能直接在另一个函数中使用。

#include <stdio.h>
#include <math.h>

// Declare the function that we're going to create
void writeHeaderInfo(); 

int main()
{
 int x = 5;
 printf("Here x is %d\n",x);
  writeHeaderInfo();
 printf("Back in main and x is %d\n",x);
}

void writeHeaderInfo()
{
  int x = 10; // Note that you can define the value of a number at the same time as stating it is e.g., an "int"
  printf("Here x = %d\n",x);
}

在上面的例子中,x 在主函数中为 5,在新函数中为 10。

可以使函数返回一个值。请注意,C 函数只能(轻松地)返回单个值。

#include <stdio.h>
#include <math.h>


// Now we're defining the function to return an integer value
int writeHeaderInfo(); 

int main()
{
 int x = 5;
 printf("Here x is %d\n",x);
 x = writeHeaderInfo();  // Note that we're now setting x to be the result from the function
 printf("Back in main and x is %d\n",x);
}

// Note we have to change the void to an int here as well
int writeHeaderInfo()
{
  int x = 10; // Note that you can define the value of a number at the same time as stating it is e.g., an "int"
  printf("Here x = %d\n",x);

  return x;  // This return statement says what value will be returned from this function
}

这应该显示 x 为 5,然后为 10,然后再次为 10。

将值传递给函数也很有用

#include <stdio.h>
#include <math.h>


// Now we're defining the function to return a floating point value
float writeHeaderInfo(int a,float b,float c); 

int main()
{
 int x = 5;
 float y = 1.2345;
 float z = pow(x,2)+sqrt(y);  // These maths functions require "math.h" to be defined
 float retVal; // Variable names do not have to be a single character (but they cannot include spaces)

 printf("Here x is %d\n",x);
 retVal = writeHeaderInfo(x,y,z);  // We're passing an integer (x) and two floats (y and z) to the function
 printf("Back in main and x is %d\n",x);
 printf("The returned value is %f\n",retVal);
}

// We're going to pass into this function an integer and two floats.  Within this function they'll be called
// a, b and c respectively
float writeHeaderInfo(int a,float b,float c)
{
  float x;
  
  x = a+b*c;

  return x; 
}

C 语言中的程序基本上从顶部开始,然后到每个函数的底部。有几种方法可以重复函数的某些部分。我们可以使用以下“for”、“while”或“do”循环。

#include <stdio.h>
#include <math.h>

int main()
{
 int x;

 for (x=10;x<20;x=x+1)
  {
   printf("x = %d\n",x);
  }
}

这应该在屏幕上打印 x = 10、x = 11 ... x = 19。首先设置 x(x=10)。然后它检查 x 是否小于 20,它是!然后它执行花括号之间的所有语句。在结束括号处,它将 x 加 1(x = x + 1),然后重新进行检查以查看 x 是否小于 20。

有一些简化。不需要写 x = x + 1;您可以写

x++;

对于 x = x + 2,您可以写

x+=2;

另一个简化是,如果循环只有一个命令,则不需要花括号

 for (x=10;x<20;x++)
   printf("x = %d\n",x);


也可以使用 while 循环

x=10;
while (x < 20)
{
  printf("x = %d\n",x);
  x++;
}

请注意,while 循环会在执行循环内的任何命令之前执行检查(x < 20)。相反,“do”循环在最后执行检查

x=10;
do 
{
  printf("x = %d\n",x);
  x++;
} while (x < 20);

条件语句

[编辑 | 编辑源代码]

通常,某些命令应该只在某些情况下运行。例如

#include <stdio.h>
#include <math.h>

int main()
{
 int x;

 for (x=10;x<20;x++)
  {
   if (x < 15)
    {
      printf("x = %d. This is less than 15\n",x);
    }
   else if (x > 17)
    {
      printf("x = %d. This is greater than 17\n",x);
    }
   else
    {
     printf("x is not less than 15 and not greater than 17\n");
    }
  }
}

同样,这可以简化为

#include <stdio.h>
#include <math.h>

int main()
{
 int x;

 for (x=10;x<20;x++)
  {
   if (x < 15)
      printf("x = %d. This is less than 15\n",x);
   else if (x > 17)
      printf("x = %d. This is greater than 17\n",x);
   else
     printf("x is not less than 15 and not greater than 17\n");
  }
}

注意

  • < 小于
  • <= 小于或等于
  • > 大于
  • >= 大于或等于
  • == 等于
  • != 不等于

而不是创建一个包含单个数字的变量,可以创建一个包含多个值(或字符)的数组。让我们分配 5 个数字

int main()
{
  int x; // This is a single number
  int y[5]; // This in an array that represents 5 numbers. Note the use of square brackets here.
  int i;

  x = 10;
  y[0] = 1;  // Note that the first element is y[0] not y[1] 
  y[1] = 2;
  y[2] = 5;
  y[3] = 10;
  y[4] = 12;

  // Now let's print them out using a loop
  for (i=0;i<5;i++)
    printf("y[%d] = %d\n",i,y[i]);
}

我们可以在循环中设置值

 for (i=0;i<5;i++)
  y[i] = i*2;

请注意,编译器不会检查您是否实际使用分配的内存。在上面的例子中,您分配了一个大小为 5 的数组,但实际上您可以写 y[100] = 10。这可能会在您运行代码时导致段错误,也可能会导致计算机崩溃!

字符串

[编辑 | 编辑源代码]

字符串是单词或短语。在 C 语言中,它们实际上是字符数组。字符存储为“char”

int main()
{
 char c;

 c = 'x';  // Set c to the character "x"  Note the single quotes.

 printf("c = %c. As an integer this character is %d\n",c,c);
}

每个字符的整数值可以在 ASCII 字符表中找到。大写字母“A”为 65,“B”= 66,等等。

要创建一个字符串,我们需要一个字符数组

int main()
{
 char c[50];

 c[0] = 'h';
 c[1] = 'e';
 c[2] = 'l';
 c[3] = 'l';
 c[4] = 'o';

 printf("My string is %s\n",c);  // Note the use of %s to represent a string and note the use of just c instead of e.g., c[0]
}

请注意,C 语言不知道字符串的实际结束位置,它可能会在您的屏幕上产生垃圾信息。您可以使用字符串结束符“\0”来解决此问题

int main()
{
 char c[50];

 c[0] = 'h';
 c[1] = 'e';
 c[2] = 'l';
 c[3] = 'l';
 c[4] = 'o';
 c[5] = '\0';

 printf("My string is %s\n",c);  
}

现在字符串应该在正确的位置停止。用这种方式创建字符串显然不切实际。相反,您可以使用一个“C”库,该库提供了用于处理字符串的函数。

#include <stdio.h>
#include <math.h>
#include <string.h>  // Note the inclusion here of string.h

int main()
{
 char c[50];

 strcpy(c,"Hello!");  // strcpy = copy a string into the variable.  Note the double quotes.
 printf("My string is %s\n",c);  

 strcat(c," a bit more"); // strcat will add another string to the end of your current string
 printf("Now c is %s\n",c); // Note that strcat doesn't check to see if you run off the end of your string

 if (strcmp(c,"boo")==0)  // strcmp is a string comparison to see if two strings are the same or not.  If they are the function returns 0.
  printf("c is boo\n");
 else
  printf("c is not boo\n");
}

还可以查看 strcasecmp、strlen 和 strstr。请注意,在 C 语言中,您不能写

c = "fred"; // This does not work

相反,您必须使用 strcpy。

获取程序的信息

[编辑 | 编辑源代码]

目前,您可以在程序中直接输入数据

x = 5;

但是,用户通常希望输入值,或者信息应该从命令行或文件获取。

从键盘获取输入

[编辑 | 编辑源代码]
int main()
{
  int x;

 printf("Please enter an integer number ");
 scanf("%d",&x);   // Note the use of an "&" here. This will be described later.
                             // %d is for an integer
                             // %f is for a float
                             // %lf is for a double (l = small L)

 printf("You entered %d\n",x);
}

对于字符串,使用

int main()
{
  char s[256];

 printf("Please enter a string ");
 scanf("%s",s);   // Note that we're not using an & symbol here - because s is an array
 printf("You entered %s\n",s);
}

请注意,使用 scanf 有一个危险。它不检查您是否实际输入了整数。您可以问“您的名字是什么?”,用户输入“hello”或“-3.23”或其他任何内容。这可能会导致程序崩溃或给出不正确的结果。在读取字符串时,您需要定义最大字符数(在上面的示例中为 256)。C 语言不会检查用户是否确实输入了少于这个字符数。如果更多,那么程序可能会崩溃。还有其他例程(例如,“gets”、“fgets”等)可以帮助解决这个问题。

命令行参数

[编辑 | 编辑源代码]

当您从命令行运行程序时,通常会输入类似以下内容

> ./myprog

您还可以传递命令行参数,例如

> ./myprog -f try.dat -k 2 -input data.input -mult 3.234

要将这些参数读入代码,请使用以下方法

int main(int argc,char *argv[])  // This * and [] will be explained later - see section on "pointers"
{  // In the above, main() is now a function that takes in two inputs. argc contains the number of command line arguments
 int i;
 printf("The number of command line arguments is %d\n",argc);
 for (i=0;i<argc;i++) 
  printf("The argument number %d is %s\n",i,argv[i]);
}

通常我们需要在一个变量中读取一个参数。例如,让我们运行

> ./myprog -k 2 -n 1.234 -title "Hello everyone"

要确定 k、n 和 title,我们需要类似以下内容

int main(int argc,char *argv[])  // This * and [] will be explained later - see section on "pointers"
{  // In the above, main() is now a function that takes in two inputs. argc contains the number of command line arguments
 int kval;
 float nval;
 char title[128];

 printf("The number of command line arguments is %d\n",argc);
 for (i=1;i<argc;i++) 
  {
    if (strcmp(argv[i],"-k")==0)
     {
       // kval = argv[i+1]; // I want to write this, but I cannot do this because kval is an integer and argv[i+1] is a string. Instead ...
       sscanf(argv[++i],"%d",&kval); // Note that sscanf is like "scanf", but reads from a string 
                                                      // Note that ++i means "add one to i before using it". i++ means "use i and then add 1"
     } 
    else if (strcmp(argv[i],"-n")==0)
       sscanf(argv[++i],"%f",&nval); 
    else if (strcmp(argv[i],"-title")==0)
       strcpy(title,argv[++i]); // Notice use of strcpy here
    else
      printf("Unknown command line argument: %s\n",argv[i]);

  printf("Our results are k = %d, n = %f, title = %s\n",kval,nval,title);
 }
   
}

读写文件

[编辑 | 编辑源代码]

假设我们有一个包含两行数字的文件。我们希望读取这些数字,将它们加在一起,并生成一个包含总和的新文件。假设输入文件名为 in.dat,其内容如下:

1 2
3 4 
5 6 
7 8
9 10

我们可以将其读入并打印到屏幕上,如下所示:

int main()
{
 FILE *fin;  // This is known as a "file pointer". Note that FILE is written in capital letters.
                 // commonly we use "fin" for file in and "fout" for file out, but you can use anything
 float x,y;  // Note that we can define two values on the same line

 fin = fopen("in.dat","r");  // The first argument is the file name.  The second "r" means that we want to "r"ead the file
 while (!feof(fin)) // This is a while loop that continues until we are not (!) at the end of the file (feof)
   {
      if (fscanf(fin,"%f %f",&x,&y)==2) // fscanf is like "scanf", but reading from a file.  The function returns the number
                                                          // of values successfully read from the file
       {
         printf("Loaded x = %f, y = %f x+y = %f\n",x,y,x+y);
       }
   }
  fclose(fin); // This will close the file
}

现在让我们将加法结果写入一个新文件:

int main()
{
 FILE *fin; 
 FILE *fout;
 float x,y,z; 

 fin = fopen("in.dat","r");  
 fout = fopen("out.dat","w"); // "w" is for writing a file. "a" is for appending to a file
 while (!feof(fin)) 
   {
      if (fscanf(fin,"%f %f",&x,&y)==2)
       {
         printf("Loaded x = %f, y = %f x+y = %f\n",x,y,x+y);
         z = x+y;
         fprintf(fout,"x + y = %f\n",z); // Note that this is exactly the same as a printf statement, but it writes the output to a file
       }
   }
 fclose(fin);
 fclose(fout); // Close the file
}

指针

[edit | edit source]

指针可能非常令人困惑。写

int x = 5;

分配一部分内存,将其标记为“x”,并存储数字 5。可以使用标签访问或修改它,例如:

printf("x = %d\n",x);
x = x + 2;

您还可以使用取地址符(&)运算符确定实际的内存位置:

int x = 5;
printf("x = %d\n",x);
printf("The address of x in memory is %x\n",&x);

传递内存位置允许您修改另一个函数中的内存片段。

void myFunc1(int y);
void myFunc2(int *y);

int main()
{
 int x = 5; 
 printf("x = %d\n",x);
 myFunc1(x);
 printf("Here x is %d\n",x);
 myFunc2(&x);
 printf("Here x = is %d\n",x);
}

void myFunc1(int y)
{
 y = 20;
}

void myFunc2(int *y) // Notice that we pass the variable using the & symbol, but here we use a "*" symbol - this is a pointer to a part of memory
{
 *y = 20; // Here y is a pointer to a part of memory.  We use the "*" to say that we want to change the value stored in the memory pointed to by "y".
}

总之,指针是一个变量(例如,它必须被声明并且它具有一个标签等),其值是另一个变量的内存地址。要声明一个指针,请使用 * 符号。要获取普通变量的地址,请使用 & 符号。

int x=5; // This is not a pointer
int *y; // This is a pointer

y = &x; // This is valid.
printf("The address of y is %x. The value in the memory at this address is %d",y,*y); // Note that the * here is used to return the value of the variable located at the address.

指针非常强大,可以使您的代码非常快。以下是如何使用指针打印出数组的元素:

int x[10]; // This is an array of 10 elements
int i;

// Let's set up this array
for (i=0;i<10;i++)
 x[i] = i*2;

// &x[0] is equivalent to x. So note that x is actually a pointer here.
// We can print out the elements using:
for (i=0;i<10;i++)
 printf("x[%d] = %d\n",i,x[i]);

// We can also use
for (i=0;i<10;i++)
 printf("x[%d] = %d\n",i,*(x+i));

结构体

[edit | edit source]

结构体提供了从 C 定义的简单变量类型(int、float、double)中脱离出来的方法。例如,脉冲星观测可能具有观测时间、频率、预拟合残差、后拟合残差等。可以使用结构体如下设置:

typedef struct psrObs {   // Define a new type of variable called a psrObs. Note that this is before the main() function
 double toa;
 double freq;
 char    telID[16];
} psrObs;

int main()
{
  psrObs obs1;
  psrObs obs2;

  obs1.toa = 53123.123;  // Notice the use of the "dot" here to set part of the structure
  obs1.freq = 1400.0;
  strcpy(obs1.telID,"Parkes");
  
  obs2.toa = 53124.54; 
  obs2.freq = 1423.0;
  strcpy(obs2.telID,"Parkes");

  // Note that you cannot simply print out a structure (learn C++ for that!)
  printf("The first toa is %lf\n",obs1.toa);
  printf("The time between the two toas is %lf\n",obs2.toa-obs1.toa);
}

如果正在使用指针,则使用 -> 符号代替 "." 符号。

typedef struct psrObs {   // Define a new type of variable called a psrObs. Note that this is before the main() function
 double toa;
 double freq;
 char    telID[16];
} psrObs;

void setObservations(psrObs *obs);  // Define a function that will set the observations

int main()
{
 psrObs obs; 
 
 setObservations(&obs);
 printf("We have a TOA at %lf\n",obs.toa); // Notice the "." here instead of the "->"
}

void setObservations(psrObs *obs)
{
 obs->toa = 51200.0;
 obs->freq = 1400.0;
 strcpy(obs->telID,"Parkes");
}

C 语言用于脉冲星天文学家

[edit | edit source]

分配大量内存

[edit | edit source]

随机数

[edit | edit source]

使用长双精度

[edit | edit source]

绘图

[edit | edit source]

使用 GSL 和 FFTW

[edit | edit source]
华夏公益教科书