脈衝星和中子星/脈衝星天文學家 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");
}