跳轉至內容

脈衝星和中子星/脈衝星天文學家 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]
華夏公益教科書