|
|
协议的分析需要参考前一篇文章以太网帧格式,IP包头,TCP头格式说明。
抓取网络上的数据包需要设置网卡为混杂模式,调用recvfrom在创建的SOCK_RAW类型的socket上接收来自kernel的信息,然后再按照帧格式,IP头,TCP头格式,指针移动到相应位置并分析。
附上的小程序由于其他原因还在UDP9001端口监听了来自客户端的消息,这与本文无关。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
#include <stdio.h> //for printf
#include <stdlib.h> //for exit
#include <string.h> // for strcmp
#include <sys/socket.h> //for socket,address
#include <sys/types.h>
#include <netinet/in.h>
#include <linux/if_ether.h>
#include <net/if.h>
#include <sys/ioctl.h> //for ioctl
#include <pthread.h>
#define INTERFACE "eth0"
#define BUFFLEN 2048
#define UDPPORT 9001
#define REQUEST 0
#define RESPONSE 1
#define RESERVE 2
#define z_print(fmt,args...) \
printf("[%s %d]"fmt"\n",__FILE__,__LINE__,##args)
typedef struct {
uint8_t version;
uint8_t type;
uint8_t len;
uint16_t number;
char data[255];
} datasend;
void* ch_hanlder();
uint8_t randvalue(); |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
| #include "zhao_sock.h"
int main(int argc,char *argv[])
{
int sockfd;
int len;
int proto;
int port_s;
int port_d;
char recvbuff[BUFFLEN];
unsigned char type[4];
unsigned char *ethhead = NULL;
unsigned char *iphead = NULL;
unsigned char *p = NULL;
struct ifreq ifr;
pthread_t thread_ch;
pthread_create(&thread_ch,NULL,&ch_hanlder,NULL);
sockfd = socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL));
if (sockfd < 0)
{
z_print("socket create error");
exit(1);
}
strcpy(ifr.ifr_name,INTERFACE);
if (ioctl(sockfd,SIOCGIFFLAGS,&ifr) == -1)
{
z_print("set PROMISC fail!\n");
exit(1);
}
ifr.ifr_flags |= IFF_PROMISC;
if (ioctl(sockfd,SIOCGIFFLAGS,&ifr) == -1)
{
z_print("set PROMISC fail!");
exit(1);
}
while(1)
{
len = recvfrom(sockfd,recvbuff,sizeof(recvbuff),0,NULL,NULL);
if (len < 42)
{
z_print("receive error!");
continue;
}
ethhead = recvbuff; //frame head point
p = ethhead;
sprintf(type,"%02x%02x",p[12],p[13]); //frame type
if (strcmp(type,"0800") != 0) //0800 is IP frame
{
if (strcmp(type,"0806") == 0)
printf("protocol: ARP\n");
else if (strcmp(type,"8035") == 0)
printf("protocol: RARP\n");
else
printf("protocol: unknow\n");
printf("MAC:%.2x:%.2x:%.2x:%.2x:%.2x:%.2x==>%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n\n", \
p[6],p[7],p[8],p[9],p[10],p[11],p[0],p[1],p[2],p[3],p[4],p[5]); //mac address
continue;
}
iphead = ethhead + 14; //ip head point
p = iphead + 12; //ip address
printf("IP:%d.%d.%d.%d==>%d.%d.%d.%d\n", \
p[0],p[1],p[2],p[3],p[4],p[5],p[6],p[7]);
proto = (iphead + 9)[0]; //ip protocol
p = iphead + 20; //ip port
switch(proto)
{
case IPPROTO_ICMP:
printf("protocol: ICMP\n");
break;
case IPPROTO_IGMP:
printf("protocol: IGMP\n");
break;
case IPPROTO_IPIP:
printf("protocol: IPIP\n");
break;
case IPPROTO_TCP:
case IPPROTO_UDP:
printf("protocol: %s\n",(proto == IPPROTO_TCP) ? "TCP":"UDP");
port_s = (p[0]<<8)&0XFF00 | p[1]&0XFF; //source port
port_d = (p[2]<<8)&0XFF00 | p[3]&0XFF; // dest port
printf("source port:%u,",port_s);
printf("dest port:%u\n",port_d);
if (port_s == 80 || port_d == 80)
printf("protocol: HTTP\n");
else if (port_s == 67 || port_d == 67)
printf("protocol: DHCP\n");
else if (port_s == 21 || port_d == 21)
printf("protocol: FTP\n");
else if (port_s == 23 || port_d == 23)
printf("protocol: TELNET\n");
else if (port_s == 53 || port_d == 53)
printf("protocol: DNS\n");
else if (port_s == 137 || port_d == 137 || port_s == 138 || port_d == 138)
printf("protocol: NetBIOS/SMB\n");
else
printf("protocol: other\n");
break;
case IPPROTO_RAW:
printf("RAW\n");
break;
default:
printf("protocol:unknow(%d)\n",proto);
}
printf("\n");
}
}
void* ch_hanlder()
{
int sockfd;
int len;
int flag_close = 0;
socklen_t addrlen;
datasend msg_send;
datasend msg_recv;
struct sockaddr_in servaddr;
memset(&msg_send,0,sizeof(datasend));
memset(&msg_recv,0,sizeof(datasend));
bzero(&servaddr, sizeof(servaddr));
msg_send.version = 1;
msg_send.type = RESPONSE;
sprintf(msg_send.data,"%s","received!");
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(UDPPORT);
addrlen = sizeof(struct sockaddr_in);
if (bind(sockfd, (struct sockaddr *)&servaddr, addrlen) == -1)
{
perror("bind error");
exit(1);
}
while (1)
{
len = recvfrom(sockfd, &msg_recv, sizeof(datasend),0,(struct sockaddr*)&servaddr, &addrlen);
printf("version:%3d\n",msg_recv.version);
printf("type:%d\n",msg_recv.type);
printf("len:%d\n",msg_recv.len);
printf("number:%d\n",msg_recv.number);
printf("data:%s\n",msg_recv.data);
if (strcmp(msg_recv.data,"close") == 0)
{
sprintf(msg_send.data,"%s","close");
flag_close = 1;
}
msg_send.number = msg_recv.number + 1;
msg_send.len = randvalue();
sendto(sockfd,&msg_send,sizeof(datasend),0,(struct sockaddr*)&servaddr,addrlen);
if (flag_close)
break;
}
return;
}
uint8_t randvalue()
{
srand(time(0));
return (rand() % 255);
} |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
| #include "zhao_sock.h"
int main(int argc, char *argv[])
{
int sockfd;
int len;
socklen_t addrlen;
struct sockaddr_in cliaddr;
FILE *fd;
datasend msg_send;
datasend msg_recv;
fd = fopen("/root/zhao_log.txt","a");
memset(&msg_send,0,sizeof(datasend));
memset(&msg_recv,0,sizeof(datasend));
bzero(&cliaddr, sizeof(cliaddr));
msg_send.version = 1;
msg_send.type = REQUEST;
msg_send.number = 1;
msg_send.len = randvalue();
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
cliaddr.sin_family = AF_INET;
cliaddr.sin_port = htons(UDPPORT);
cliaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
addrlen = sizeof(struct sockaddr_in);
while (1)
{
printf("please input send msg:");
scanf("%s",msg_send.data);
sendto(sockfd,&msg_send,sizeof(datasend),0,(struct sockaddr*)&cliaddr,addrlen);
len = recvfrom(sockfd, &msg_recv, sizeof(datasend),0,(struct sockaddr*)&cliaddr, &addrlen);
msg_send.number = msg_recv.number + 1;
msg_send.len = randvalue();
printf("version:%3d\n",msg_recv.version);
printf("type:%d\n",msg_recv.type);
printf("len:%d\n",msg_recv.len);
printf("number:%d\n",msg_recv.number);
printf("data:%s\n",msg_recv.data);
fprintf(fd,"version:%d\n",msg_recv.version);
fprintf(fd,"type:%d\n",msg_recv.type);
fprintf(fd,"len:%d\n",msg_recv.len);
fprintf(fd,"number:%d\n",msg_recv.number);
fprintf(fd,"data:%s\n",msg_recv.data);
if (strcmp(msg_recv.data,"close") == 0)
break;
}
fclose(fd);
return 0;
}
uint8_t randvalue()
{
srand(time(0)+1);
return (rand() % 255);
} |
 圖三、乙太網路的 MAC 訊框
在這個 MAC 當中,最重要的就是那個 6 Bytes 的目的與來源位址了! 事實上,在所有的乙太網路卡當中都有一個獨一無二的網路卡卡號, 那就是上頭的『目的與來源位址』,這個位址是硬體位址( hardware address ), 共有 6 bytes ,分別由 00:00:00:00:00:00 到 FF:FF:FF:FF:FF:FF, 這 6 bytes 當中,前 3bytes 為廠商的代碼,後 3bytes 則是該廠商自行設定的裝置碼了。 在 Linux 當中,你可以使用 ifconfig 這個指令來查閱你的網路卡卡號喔! 不過,由於 MAC 主要是與網路卡卡號有關,所以我們也常常將 MAC 作為網路卡卡號的代稱。 特別注意,在這個 MAC 的傳送中,他僅在區域網路內生效, 如果跨過不同的網域 (這個後面 IP 的部分時會介紹),那麼來源與目的的位址就會跟著改變了。 這是因為變成不同網路卡之間的交流了嘛!所以卡號當然不同了!如下所示:

圖四、在不同主機間持續傳送相同資料的 MAC 訊框變化
例如上面的圖示,我的資料要由電腦 A 通過 B 後才送達 C ,而 B 電腦有兩塊網路卡, 其中 MAC-2 與 A 電腦的 MAC-1 互通,至於 MAC-3 則與 C 電腦的 MAC-4 互通。 但是 MAC-1 不能與 MAC-3 與 MAC-4 互通,為啥?因為 MAC-1 這塊網路卡並沒有與 MAC-3 及 MAC-4 使用同樣的 switch/hub 相接嘛!所以,資料的流通會變成:
- 先由 MAC-1 傳送到 MAC-2 ,此時來源是 MAC-1 而目的地是 MAC-2;
- B 電腦接收後,察看該訊框,發現目標其實是 C 電腦,而為了與 C 電腦溝通, 所以他會將訊框內的來源 MAC 改為 MAC-3 ,而目的改為 MAC-4 ,如此就可以直接傳送到 C 電腦了。
也就是說,只要透過 B (就是路由器) 才將封包送到另一個網域 (IP 部分會講) 去的時候, 那麼訊框內的硬體位址就會被改變,然後才能夠在同一個網域裡面直接進行 frame 的流通啊!
MAC包大小:旧为1900bytes,大为9000bytes
IP 封包的表頭
現在我們知道 IP 這個資料封包 (packet) 是需要放置在 MAC 訊框裡面的,所以當然不能比 MAC 所能容許的最大資料量還大!但是 IP 封包其實可以到 65535 bytes 那麼大的吶! 那麼 IP 封包除了資料之外,他的表頭資料 (head) 是長怎樣呢? 在 圖三的 MAC 訊框表頭裡面最重要的莫過於那個網路卡硬體位址, 那麼在 IP 表頭裡面當然就以來源與目標的 IP 位址為最重要囉! 除此之外, IP 表頭裡面還含有哪些重要資料呢?如底下所示:(下圖第一行為每個欄位的 bit 數)

圖八、IP 封包的表頭資料
在上面的圖示中有個地方要注意,那就是『 每一行所佔用的位元數為 32 bits』, 也就是說, IP 封包的表頭資料是 32 bits 的倍數喔!那各個表頭的內容分別介紹如下:
继续阅读
发现很多公司面试都喜欢考链表,实际项目中尽管需要自己写链表的时候比较少,但还是会经常用到,链表是一种常用的数据结构。有必要整理一下自己的思路,索性写了一个。包括了链表的初始化,插入,删除,查找,销毁,打印等基本功能,当然考虑了头尾中间等位置情况。仓促完成的简单代码,bug比较多,链表中只存储了一个整形的变量,没有做数据类型的判断,有时候会段错误也难免。已知的一个问题就是创建的链表在有销毁前如果再创建一个的话,会造成内存泄露,因为之前的链表没有销毁头指针便指向了新的位置,旧链表就永远找不到哦。这些问题留在以后实际用到的时候再考虑吧,应付面试这个足矣!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
| #include <stdio.h>
#include <stdlib.h>
typedef struct node_t {
int value;
struct node_t *next;
} node;
#define LENGTH sizeof(node)
node *init_node(){
int node_length,n=0;
node *head=NULL;
node *node_temp1,*node_temp2;
node_temp2=(node *)malloc(LENGTH);
printf("node length want to create:");
scanf("%d",&node_length);
if(node_length<1){
printf("no node to create!");
exit(0);
}
while(n<node_length){
node_temp1=(node *)malloc(LENGTH);
printf("input NO.%d value:",n+1);
scanf("%d",&node_temp1->value);
node_temp1->next=NULL;
if(n==0)
head=node_temp1;
else
node_temp2->next=node_temp1;
node_temp2=node_temp1;
n++;
}
return head;
}
node *insert_node(node *head){
int insert_value;
node *temp_node1=head;
node *temp_node2=(node *)malloc(LENGTH);
node *temp_node3=NULL;
printf("insert value:");
scanf("%d",&insert_value);
while(temp_node1!=NULL){
if(temp_node1->value<insert_value){
temp_node3=temp_node1;
temp_node1=temp_node1->next;
}
else
break;
}
temp_node2->value=insert_value;
if(temp_node3==NULL){/*locate the first node*/
temp_node3=head;
temp_node2->next=temp_node3;
head=temp_node2;
}
else if(temp_node1!=NULL){ /*locate the medium of head,insert between temp3 and temp1*/
temp_node2->next=temp_node3->next;
temp_node3->next=temp_node2;
}else{ /*locate the end of head*/
temp_node2->next=NULL;
temp_node3->next=temp_node2;
}
return head;
}
node *delete_node(node *head){
int del_value;
node *temp_node1=head;
node *temp_node2;
printf("input the delete value:");
scanf("%d",&del_value);
if(head->value==del_value){ /* delete the head of node*/
temp_node2=head;
head=head->next;
return head;
}
while(temp_node1!=NULL){
if(temp_node1->value!=del_value){
temp_node2=temp_node1;
temp_node1=temp_node1->next;
}
else
break;
}
if(temp_node1!=NULL){
temp_node2->next=temp_node1->next;
free(temp_node1);
}else
printf("not find what you want to delete!\n");
return head;
}
void search_node(node *head){
int search_value,n=1;
node *temp_node=head;
printf("insert the value which you find:");
scanf("%d",&search_value);
while(temp_node!=NULL){
if(temp_node->value!=search_value){
temp_node=temp_node->next;
n++;
}
else
break;
}
if(temp_node!=NULL)
printf("finded what you want.\nthe %d node`s value:%d\n",n,temp_node->value);
else
printf("not find!\n");
}
void print_node(node *head){
if(head==NULL){
printf("blank node!create first please!\n");
return;
}
node *temp_node=head;
do{
printf("value:%d\n",temp_node->value);
temp_node=temp_node->next;
} while(temp_node!=NULL);
}
node *destroy_node(node *head){
if(head==NULL){
printf("blank node!create first please!\n");
return;
}
node *temp_node;
do{
temp_node=head->next;
free(head);
head=temp_node;
}while(head!=NULL);
return head;
}
int main(){
int options;
node *head=NULL;
loop:
printf("select options:\t1:create 2:insert 3:delete 4:find 5:print 6:destory 7:exit\ninput options:");
scanf("%d",&options);
switch(options){
case 1:
head=init_node();
break;
case 2:
head=insert_node(head);
break;
case 3:
head=delete_node(head);
break;
case 4:
search_node(head);
break;
case 5:
print_node(head);
break;
case 6:
head=destroy_node(head);
break;
case 7:
printf("byebye!");
return 0;
default:
printf("invalid options!");
}
goto loop;
} |
如果上次的更换工作不算跳槽的话,这次的得算了。
简历改了一遍又一遍,拿着改满意的简历再和刚毕业那会的对比,发现了很多的不同。
主修过N多的课程统统的删掉了,因为发现很多名字都已经有些生疏了;带熟悉字眼的也统统干掉了,其实自己什么都不懂,难道会写个hello就说英语牛X了;精通的字眼大都改成了熟悉、了解,真佩服自己当年的肺活量,太能吹了;实话实说的写了一段自己做过什么,这才是最真实的一段了。
进了这个圈才知道,自己能圈住的原来寥寥无几。随着慢慢的圈住一些东西,才懂得自己应该圈住哪些,外面的世界太大了,抓住适合自己的,真正为我所用的才是关键。
虽说简历要展现自己,做的多么华丽,写的多么坚决,但现在看起来,这些指导让我颇有些心虚,为难。
拿着这份简历面试了一下午,这都是好几周前的事了。现在敢写出来也是因为已经不属于那里,也就不用怎么顾忌。其实人家看的也就是自己觉得最真实的那段。聊的很多东西都是关于具体的项目,职业的规划。看来刚毕业的一大段只能归结为凑字数。
曾经就听到之前的经理闲谈之间说到,凡是说精通的,顶多算个了解,说了解的,大概也就知道个名字。真是悲哀,大家心知肚明,却也只能这样。
真实过后,等我再去吹嘘的时候,应该能吹一个更大,大好几倍的泡泡,想想都觉得刺激。继续努力,鼓励自己了!
刚刚办完了离职手续,再有一会就离开这里了
一年多的时光,接触到了很多领域,还是非常感谢公司对我的培养!
祝愿公司日益强大,越来越好!
朝着自己的方向,继续前进了!
摘自网络,感谢原作者
摘要:
本文试图成为学习TCP/IP网络组播技术的入门材料。文中介绍了组播通信的概念及原理,以及用于组播应用编程的Linux API的详细资料。为了使读者更加完整的了解Linux 组播的整体概念,文中对实现该技术的核心函数也做了介绍。在文章的最后给出了一个简单的C语言套接字编程例子,说明如何创建组播应用程序。
一、导言
在网络中,主机间可以用三种不同的地址进行通信:
单播地址(unicast):即在子网中主机的唯一地址(接口)。如IP地址:192.168.100.9或MAC地址:80:C0:F6:A0:4A:B1。
广播地址:这种类型的地址用来向子网内的所有主机(接口)发送数据。如广播IP地址是192.168.100.255,MAC广播地址:FF:FF:FF:FF:FF。
组播地址:通过该地址向子网内的多个主机即主机群(接口)发送数据。
如果只是向子网内的部分主机发送报文,组播地址就很有用处了;在需要向多个主机发送多媒体信息(如实时音频、视频)的情况下,考虑到其所需的带宽,分别向每一客户端主机发送数据并不是个好办法,如果发送主机与某些接收端的客户主机不在子网之内,采用广播方式也不是一个好的解决方案。
继续阅读
使用ioctl的SIOCGIFCONF可以读取所有网卡信息。ioctl调用后返回指向ifconf的结构链表,其中包含了指向ifreq的结构指针。ifconf及ifreq定义在net/if.h中。
《UNIX网络编程》中提供了get_ifi_info函数的实现方法,使用这种方式来获取网络信息。在LINUX下,这种方式不能获得IPV6的网卡信息。《UNIX网络编程》中有如下描述:
在支持IPV6的系统中,没有关于对SIOCGIFCONF请求是否返回IPV6地址的标准。我们给支持IPV6的新系统增加了一个case语句, 这是为了预防万一。问题在于ifreq中的联合把返回的地址定义成一个通用的16字节套接口地址结构,适合16字节的IPV4 socket_in结构,但对于24字节的IPV6 socket_in6结构太小了。如果返回IPV6地址,将可能破环现有的在每个ifreq结构中采用固定大小的套接口地址结构的代码。
经测试,在fedor6-2.6.18kernel中无法返回ipv6地址,事实上,返回的地址簇总是AF_INET,而并非AF_INET6。
这种方法的实现代码如下:
继续阅读
今天还真是郁闷,坐公交没有零钱了,就跟司机师傅说了一声,放5块钱进去,司机师傅同意让我在门口收三块零钱。收了两块还顺利,就差一块钱了,比较背的事情就发生了。一个女孩上来正要投硬币,我说了句找零钱,给我一个。大概是她没听清,也怪我没说清了。好像犹豫了一下还是把两个硬币都扔进了收款的柜子。我几乎无语,也再没说什么。谁知她开始翻自己的包,我还正纳闷她干嘛呢,只见她抬起头来说了一句没零的了,向后边走去了。
愣了我半天,憋出来一句喃喃了一下,把我当什么了?有穿成这样站司机旁边大摇大摆乞讨的么?
连接两个局域网的方法除了路由外,比较简单的一个方法就是网络桥接了。这里将列出linux下创建网络桥接的一般步骤。
如下图的所示的网络环境中,中间的桥接计算机具有无线和有线网卡各一块,连接了两个局域网。在这个网络环境中两个局域网处于同一个网段,它可能是由桥接计算机上的DHCP自动分配的地址。这样做,最终产生的情况将是无线终端连接到了桥接计算机的无线网卡ath0(ath0工作在AP模式),有线网局域网通过交换机连接到了桥接计算机的eth0(有线网卡),连接在不同的两个物理网络上,通过桥接,使他们工作在同一个局域网中,同时,可以隔离两个物理网络,这是网络隔离中比较常见的手段。
linux下使用桥接功能必须确保已经安装了bridge-utils,桥接方法如下:
1.创建br0: brctl addbr br0
2.添加物理网口:brctl addif br0 eth0; brctl addif br0 ath0
3.配置br0 IP: ifconfig br0 192.168.1.100 netmask 255.255.255.0 up
4. 启用物理网络:ifconfig ath0 up; ifconfig eth0 up
5.修改DHCP配置,使之在br0网口上分配地址。
如需要,可以用iptables来隔离两个物理网络。
const限定符指定了一个变量为只读变量,是不允许被改变的,因此const变量在定义时就必须初始化。
const在与指针搭配时,使用将变的复杂和微妙。简单的说const搭配指针就会出现以下三种情况:
- 指向const变量(对象)的指针
- const指针
- 指向const变量(对象)的const指针
1.指向const变量(对象)的指针
指针指向了const变量,例如 const int *ptr或者int const *ptr,这两种写法含义一样,这表示const限定了ptr所指向的数据类型,而并非ptr本身。即ptr本身并不是const.可以对ptr重新赋值,无需在定义时初始化。
指向const变量的指针可以指向一个const变量,也可以指向一个非const变量,当然指针类型与变量类型要一致。不管指向了一个const变量还是非const变量,任何企图通过这个指针去修改变量的值都会导致编译错误。同时,const变量只能赋给指向const变量的指针,赋给一个普通变量也是不允许的。
继续阅读
|
|
COMMENTS