脉冲星和中子星/脉冲星天文学家 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");
}