我覺著你可能不是相對路徑,相對路徑默認的圖片路徑都會被編譯
檢查以下是不是圖片路徑寫的是/img/pic1.jpg
PHP腳本當接到POST請求時,$_POST數(shù)組就會被自動創(chuàng)建并裝入請求的參數(shù)。所以在整個腳本,甚至其中引用的腳本,都能夠訪問到同樣的請求參數(shù)。如果提示Undefined index有可能是前端傳入的參數(shù)中并不包含password這個字段。你可以在腳本一開始查看一下file_get_contents('php://input')這條語句的返回值,里面包含了請求參數(shù)的原始內(nèi)容,是不是少了password
你先不用5個數(shù),我給你個測試數(shù)據(jù):[2,1,3]
你自己在紙上跟著程序單步跑一跑你就明白你問題出來在哪了。
二級指針, 詳見我以前的這篇博文, 看完了你應(yīng)該就有答案了http://czxyl.me/2017/05/14/In...
以防被墻, 在這里貼出原文.
title: Introduction to double pointers---part1
date: 2017-05-14 19:48:03
tags:
thumbnail: https://d3nmt5vlzunoa1.cloudf...
categories: [c/c++, tricky]
[TOC]
這篇文章試圖從最簡單的例子入手展開指針的實用的技能. 考慮到這篇文章的某些讀者可能不是很熟悉c語言或者對指針仍然不是很熟悉, 所以我們先來看3個非常簡單的例子來熱下身, 不過為了增加點樂趣, 這里也會列出常見的一些錯誤. 不過為了保證文章的流暢性, 關(guān)于指針的語法細節(jié)我不會多涉及. 雖然很多例子都可以改用reference而不是pointers, 但是這里用pointers能更好的講解, 有興趣的同學(xué)也可以自己嘗試用reference改寫例子, 畢竟多動手時提高的唯一方式啊... 本文采取的書寫方式主要是用問答式, 并且盡量將問題進行分解, 也就是說每次提問的答案都很簡短, 但是問題會因此增多, 也會反復(fù)強調(diào)些要點. 所以希望大家看到一個問題先做思考. 當然如果大家發(fā)現(xiàn)我的答案和我的提問不匹配時希望能留言或者發(fā)郵件至czxyl@protonmail.com給我一個改正的機會, 謝謝.
int main()
{
int *x = new int(1);
int **x_address = &x;
}
Q0.1: 這里的x和x_address代表什么?
answer: x 代表1
這個rvalue的地址, 也就是說它的值是就是一個整數(shù)的地址 或者說x這個地址上存儲著一個int 1
, 如果我們對x進行dereference
, 得到的值就是1了 x_address
代表著x這個地址的地址. 如果我們對x_address
進行dereference
, 那么得到的值就是x, 也就是一個整數(shù)的地址了. 如果再進一步dereference
得到的就是1了. 所以本文就是圍繞著這么個最簡單的知識展開的.
void swap(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
void swap(int *a, int *b)
{
int *temp = a;
a = b;
b = temp;
}
void swap(int **a, int **b)
{
int ** temp = a;
a = b;
b = temp;
}
void swap(int a, int b)
{
int temp = a;
a = b;
b = temp;
}
answer: A是對的, B, C, D是錯的.
answer: 很明顯, A交換了a和b指向的address之上的value. 而B交換的是兩個address本身,
然而其上的value依然沒有變.
answer: 懂了B的錯誤后, 很輕松的就能發(fā)現(xiàn)C里面交換的只是地址的地址. 所以最頂層的value依然是不變的. 這里提這個形式的例子主要是為了引出主題(double pointer)
void alloc1(int* p) {
p = (int*)malloc(sizeof(int));
*p = 10;
}
int main(){
int *p = NULL;
alloc1(p);
printf("%d ",*p);
free(p);
return 0;
}
void alloc2(int** p) {
*p = (int*)malloc(sizeof(int));
**p = 10;
}
int main(){
int *p = NULL;
alloc2(&p);
printf("%d ",*p);
free(p);
return 0;
}
answer: A錯B對
answer: 1. 無法準確打印出*p
的值. 2. 會有內(nèi)存泄漏
answer: 都妄圖通過傳value
來改變value
, 注意, 此處我定義的value
是廣義上的, 就像allocate::version A傳入的是一個指針int *p
, 同時接受的也是int *
類型, 所以我在這里統(tǒng)一看做傳value
. 與之相對的就是allocate::version B的傳參就是傳address
了.
answer: 使用function
來改變值時都傳入的是其地址.
*p
的值?answer: 結(jié)合Q2.3, 可以看出其實p是作為一個值傳入的. 自然alloc1()里面的p再怎么分配malloc也和main()中的p沒有關(guān)系了. 同時, 由于alloc里面分配的內(nèi)存的所有權(quán)并沒有返回轉(zhuǎn)交到main()中, 所以會造成p = (int*)malloc(sizeof(int));
出來的內(nèi)存就被拋棄了, 造成內(nèi)存泄漏
answer: 當然, 這里比較直觀, 所以能肉眼看出來, 但是程序一旦復(fù)雜, 想檢測這種內(nèi)存泄漏的情況可就不一定能一眼看出來了. 所以我們需要一些工具, 比如vs下的_CrtDumpMemoryLeaks
或者linux下的valgrind
.
answer: 首先, 傳入的是一個int*
的地址, 也可以說成pointer to pointer
. 然后在這個地址的值(本質(zhì)也是一個地址)上開辟了空間*p = (int*)malloc(sizeof(int));
. 因此相當于在swap時需要往下探入一層(從value到address), 這里也是在地址上的基礎(chǔ)上再往下探入到地址的地址, 這樣地址能夠被在main()中的p保存. 因此allocate2()中對**p的修改也能在main()中反映.
我們想要通過函數(shù)修改任何一個值必須傳入它地址. 而不能僅僅傳入值, 即使它本身是一個地址, 而你想要修改的就是這個地址, 那么你需要做的就是傳入這個地址的地址. 不過寫到這里, 讀者可能還是不知道什么時候需要用到這種double pointers
. 所以, 接下來我們通過各種例子來展示下此技巧的威力
這里的insert是在尾部插入結(jié)點, 并且為了講解的效果, 這個例子不使用返回值, 統(tǒng)統(tǒng)使用void. 并且以下某些結(jié)論也是在僅使用void得出的. 如果返回head的話就沒討論的意義了......
void insert(node *head, node *inserted_node) //假設(shè)head是鏈表頭, inserted_node是帶插入尾部的已分配空間的結(jié)點
{
node *current_node = head;
if (current_node == NULL)
{
head = inserted_node;
}
else
{
while (current_node->next)
{
current_node = current_node->next;
}
current_node->next = inserted_node;
}
}
void insert(node **head, node *inserted_node)
{
node **current_node = head;
while (*current_node)
{
current_node = &(*current_node)->next;
}
*current_node = inserted_node;
}
void insert(node *head, node *inserted_node)
{
node *current_node = head;
while (current_node)
{
current_node = current_node->next;
}
current_node = inserted_node;
}
if
部分的判斷會怎么樣?answer: 插入第一個結(jié)點(頭結(jié)點)時會訪問到NULL->next
, 非法內(nèi)存訪問.
if
條件?answer: 想要回答這個問題, 只需要考慮version B在插入頭結(jié)點時的是如何處理的就行了. 與version A不同, version B是用*current_node
做循環(huán)條件的, 而不是(*current_node)->next
. 這樣做的好處是避免了NULL->next
這樣的錯誤步驟出現(xiàn).
while (current_node)
來避免使用if條件, 也就是version C這樣的嗎?answer: 我們來看下完整的代碼, 希望讀者能上機運行下
#include "stdafx.h"
class node
{
public:
node *next;
int elem;
node(int e) : next{NULL}, elem{e} {}
};
void insert(node *head, node *inserted_node)
{
node *current_node = head;
while (current_node)
{
current_node = current_node->next;
}
current_node = inserted_node;
}
int main()
{
node *head = NULL;
node *inserted_node = new node(1);
insert(head, inserted_node);
inserted_node = new node(2);
insert(head, inserted_node);
return 0;
}
首先我們必須要搞清楚我們傳參的目的是什么, 以上面這份代碼為例, 我們的第一個insert其實是需要修改head的value, 但是, 我們我們傳入的其實也是head的value, 回想下文章開始的case1, case2, 毫無疑問, 這樣做的話, main()中的head依然是NULL, 得不到更新. 并且一直到return 0
, head保持著NULL不變. 自然而然我們會想到把node *head = NULL
替換為node head(you_choose_integer)
. 這樣做的話其實是和其它version邏輯稍有區(qū)別的, 即這里的頭結(jié)點是在main()里直接給出, 而不是insert中插入一個頭結(jié)點. 當然, 后面的邏輯依然是一致的. 下面給出完整代碼后我們繼續(xù)分析問題(依然沒有析構(gòu)僅供這里參考下)
#include "stdafx.h"
class node
{
public:
node *next;
int elem;
node(int e) : next{NULL}, elem{e} {}
};
void insert(node *head, node *inserted_node) //假設(shè)head是鏈表頭, inserted_node是帶插入尾部的已分配空間的結(jié)點
{
node *current_node = head;
if (current_node == NULL)
{
head = inserted_node;
}
else
{
while (current_node->next)
{
current_node = current_node->next;
}
current_node->next = inserted_node;
}
}
int main()
{
node head(0);
node *inserted_node = new node(1);
insert(&head, inserted_node);
inserted_node = new node(2);
insert(&head, inserted_node);
return 0;
}
好, 邏輯上終于修改完善了, 并且正確性大家上機測下很容易 我們以上面這份代碼來看Q1.3本身. 首先我們將代碼改為問題描述的樣子
#include "stdafx.h"
class node
{
public:
node *next;
int elem;
node(int e) : next{NULL}, elem{e} {}
};
void insert(node *head, node *inserted_node) //假設(shè)head是鏈表頭, inserted_node是帶插入尾部的已分配空間的結(jié)點
{
node *current_node = head;
while (current_node->next)
{
current_node = current_node->next;
}
current_node->next = inserted_node;
}
int main()
{
node head(0);
node *inserted_node = new node(1);
insert(&head, inserted_node);
inserted_node = new node(2);
insert(&head, inserted_node);
return 0;
}
首先看參數(shù)傳遞, 的確傳的是要修改的值的地址, 然后, 因為main()中初始時有一個非NULL
的頭結(jié)點, 所以 while (current_node->next)
不會出錯. 下面的問題我會進一步講解這種寫法可能出錯的地方. 但是這個其實是特例, 畢竟很少有這種把head一開始就定義的情況...不過下面的一些例子還是會讓大家看到有些情況double pointer
能夠?qū)懗霰?code>singal pointer更簡練的代碼的.
while (current_node->next)
改為while (current_node)
會怎么樣?answer: 這樣最后跳出時我們得到current_node
是
NULL, 看上去是合理的, 給那個NULL賦值inserted_node
就行了. 但是又有一個新問題, 給這個NULL賦值后我們能確保prev->next = inserted_node
嗎? 這里其實是很反直覺的, 答案是不能, 首先我們要知道了next實際上都指向?qū)嶋H的內(nèi)存地址, 而當current_node = NULL
(循環(huán)終止條件)時, 我們再給它進行復(fù)制, prev結(jié)點的next依然是NULL, 因為在這里current_node
只是一個獨立的指針, 與鏈表結(jié)構(gòu)已經(jīng)無關(guān)了. 如果我們一定要這么做, 必須維護著一個實際存在的prev結(jié)點.
answer:
其實這個問題的依舊隱藏著一個前提條件, 就是這樣修改依舊會實現(xiàn)在main()中定義好了一個頭結(jié)點, 這樣的話即使形參實參都是node*類型, 也就是說沒有傳地址的情況, 依舊是正確的. 因為它不需要改變head或者inserted_node, 只需要改變next就行了. 但是一旦寫成node *head = NULL
, 就會犯NULL->next這樣的read access violation
錯誤了.
answer: 沒有, 我們討論兩者情況,
法1的問題是想修改head(題目要求)但是傳入的是node *head
, 自然不能成功
法2的問題是這樣必須在main()中建立了一個value是0的頭結(jié)點了, 所以已經(jīng)與題意不符了.
answer: 我們首先再回顧下(別怪我啰嗦, 我認為這是最重要的), 傳入的是要修改的head
的地址(別被形參和實參名一樣所迷惑, 他們代表的是兩個不同的東西, 形參是實參的地址). 我們先看對頭結(jié)點的處理: 將NULL的地址傳入, 并且修改為了inserted_node
. 然后看非head結(jié)點的建立, 此時就需要用到while了. 我們分析下這個while的含義, 循環(huán)條件是對current_node進行dereference
, 判斷是不是NULL. 循環(huán)執(zhí)行語句是每次講current_node這個地址的地址下移(next). 邏輯還是很清楚的.
answer: 可以任性的在main()中設(shè)置head, 比如node *head = NULL
, 也可以node head(0)
, 也可以node *head = new node(0)
. 其他version就有著這樣那樣的限制.
這里remove是指移除滿足一定條件的結(jié)點, 不限重復(fù). 在下面的例子中就是移除num是奇數(shù)的結(jié)點. 沒有處理析構(gòu), 僅寫了兩份簡單代碼以便講解. 如果有不了解lambda的可以看version A, C, 不了解函數(shù)指針的可以看version B, D. 不過并不是c style的就是純粹的c了, 比如初始化列表什么的還是用的c++. 還有一點就是這里的single pointer不返回void了. 畢竟上面關(guān)于void的情況依舊討論了很多了, 沒必要繼續(xù)了. 雖燃insert的例子講了那么多關(guān)于值與地址, 但是僅僅為了更好的使讀者理解double pointers, 實際上在不使用void的情況下我們完全可以返回一個head來維護head, 即使傳入的是其value, 只要head = remove_if(...)來接受返回值就行了. 所以再接下來的講解中我們不過多關(guān)注value和address, 而是關(guān)注于代碼邏輯.
#include "stdafx.h"
#include <iostream>
typedef struct node
{
struct node * next;
int num;
node(int x) : next{ NULL }, num{ x } {}
} node;
typedef bool(*remove_fn)(node const * v);
void remove_if(node ** head, remove_fn rm)
{
for (node** curr = head; *curr; )
{
node * entry = *curr;
if (rm(entry))
{
*curr = entry->next;
delete entry;
}
else
curr = &entry->next;
}
}
bool find_odd(node const * x)
{
if (x->num % 2 == 0)
{
return false;
}
return true;
}
int main()
{
node *head = new node(1);
head->next = new node(2);
head->next->next = new node(3);
head->next->next->next = new node(4);
remove_fn rm = find_odd;
remove_if(&head, rm);
return 0;
}
#include "stdafx.h"
#include <iostream>
struct node
{
struct node * next;
int num;
node(int x) : next{ NULL }, num{ x } {}
};
template<typename remove_fn>
void remove_if(node ** head, remove_fn rm)
{
for (node** curr = head; *curr != NULL; )
{
node * entry = *curr;
if (rm(entry))
{
*curr = entry->next;
delete entry;
}
else
curr = &entry->next;
}
}
int main()
{
node *head = new node(1);
head->next = new node(2);
head->next->next = new node(3);
head->next->next->next = new node(4);
remove_if(&head, [&](node* temp) {return temp->num % 2 != 0; });
return 0;
}
#include "stdafx.h"
#include <iostream>
typedef struct node
{
struct node * next;
int num;
node(int x) : next{ NULL }, num{ x } {}
} node;
typedef bool(*remove_fn)(node const * v);
node * remove_if(node * head, remove_fn rm)
{
for (node * prev = NULL, *curr = head; curr != NULL; )
{
node * const next = curr->next;
if (rm(curr))
{
if (prev)
prev->next = next;
else
head = next;
delete curr;
}
else
prev = curr;
curr = next;
}
return head;
}
bool find_odd(node const * x)
{
if (x->num % 2 == 0)
{
return false;
}
return true;
}
int main()
{
node *head = new node(1);
head->next = new node(2);
head->next->next = new node(3);
head->next->next->next = new node(4);
remove_fn rm = find_odd;
head = remove_if(head, rm);
return 0;
}
#include "stdafx.h"
#include <iostream>
struct node
{
struct node * next;
int num;
node(int x) : next{ NULL }, num{ x } {}
};
template<typename remove_fn>
node * remove_if(node * head, remove_fn rm)
{
for (node * prev = NULL, *curr = head; curr != NULL; )
{
node * const next = curr->next;
if (rm(curr))
{
if (prev)
prev->next = next;
else
head = next;
delete curr;
}
else
prev = curr;
curr = next;
}
return head;
}
int main()
{
node *head = new node(1);
head->next = new node(2);
head->next->next = new node(3);
head->next->next->next = new node(4);
head = remove_if(head, [&](node *temp) {return temp->num % 2 != 0; });
return 0;
}
#include "stdafx.h"
#include <iostream>
struct node
{
struct node * next;
int num;
node(int x) : next{ NULL }, num{ x } {}
};
template<typename remove_fn>
node * remove_if(node * head, remove_fn rm)
{
for (node* curr = head; curr; )
{
node * entry = curr;
if (rm(entry))
{
curr = entry->next;
delete entry;
}
else
curr = entry->next;
}
return head;
}
int main()
{
node *head = new node(1);
head->next = new node(2);
head->next->next = new node(3);
head->next->next->next = new node(4);
head = remove_if(head, [&](node *temp) {return temp->num % 2 != 0; });
return 0;
}
answer:
answer: refer to version C/D: 如果prev是NULL, 那么就該維護head了(head = next). 使用prev->next = next
, prev = curr
來維護羈絆. next還有一個作用就是在delete curr前保存著下一個結(jié)點, 如果貪心省略掉這個next 你還是需要一個temp來保存的, 反而變丑了. 這也是寫代碼時容易忽略的一點.
answer: single pointer version都需要維護prev和next結(jié)點, 而double pointers僅需要維護一個entry結(jié)點(除curr, 并且本質(zhì)上entry起著prev的作用). single pointer需要多一個判斷head的操作
answer: 這個問題依然要回到之前反復(fù)強調(diào)的value-address了, 傳入的head在這里是可以被修改的, 所以能在if里面直接維護.
answer: 這個問題也需要回到value-address來回答. 其實無論是head還是non-head, 處理方式都是一樣的----改變這個結(jié)點的地址為->next. 這樣是不會破壞與prev的羈絆的. 即使是NULL都無妨(ctrl/command f 任性
, 參考這里).
單鏈表的排序里面我覺得插排是最容易實現(xiàn)的, 所以在這里我使用它來講解 single pointer VS double pointers
.
ListNode* linkedListSort(ListNode *head) {
if (head == NULL || head->next == NULL)
{
return head;
}
struct ListNode *new_head = new struct ListNode(head->val); // 新鏈表的頭結(jié)點
struct ListNode *current_node = head->next; // 遍歷舊鏈表
struct ListNode *tail_node = new_head; // 新鏈表的最后一個結(jié)點
while (current_node != NULL)
{
struct ListNode *temp_node = new_head; // 在新鏈表上進行查找
if (current_node->val < tail_node->val) // 需要進行中間插入的情況, 為了維護tail_node, 這里不能是小于等于
{
if (new_head->val >= current_node->val) //比頭結(jié)點還小的情況
{
struct ListNode *new_new_head = new struct ListNode(current_node->val); // 新的頭結(jié)點
new_new_head->next = new_head;
new_head = new_new_head;
}
else if (new_head->next->val >= current_node->val) // 比頭結(jié)點的下一個結(jié)點小但比頭結(jié)點大
{
struct ListNode *after_new_head = new struct ListNode(current_node->val);
after_new_head->next = new_head->next;
new_head->next = after_new_head;
}
else // 比頭結(jié)點和第二個結(jié)點都大的情況
{
while (temp_node->next->val <= current_node->val)
{
temp_node = temp_node->next;
}
struct ListNode *middle_insert_node = new struct ListNode(current_node->val);
middle_insert_node->next = temp_node->next;
temp_node->next = middle_insert_node;
}
}
else //直接放尾部的情況
{
struct ListNode *new_tail_node = new struct ListNode(current_node->val);
tail_node->next = new_tail_node;
tail_node = new_tail_node;
}
current_node = current_node->next;
}
return new_head;
}
我的這個鏈表插排的寫法看起來的確很長, 但是勝在邏輯比較清楚, 可讀性很強, 邊界條件也都處理的比較完善. 所以這也是我少有的用沒有智能提示的editor里寫一遍就能通過編譯的代碼, 如果讀者抱著學(xué)習(xí)的態(tài)度來看這篇文章但是上面那些例子都沒有自己動手寫, 那么希望能親自動手寫下這個問題, 為了方便大家盡快理解思路以免浪費時間, 我在關(guān)鍵位置都寫了詳細的注釋.
answer:
ListNode* linkedListSort(ListNode *head) {
// 很清楚的可以看到是O(n)復(fù)雜度
if(head == NULL || head->next == NULL)
{
return head;
}
struct ListNode * new_head = NULL;
while (head != NULL)
{
struct ListNode * copy_of_head = head;
struct ListNode ** pointer_to_new_head = &new_head;
head = head->next;
while (!(*pointer_to_new_head == NULL || copy_of_head->val < (*pointer_to_new_head)->val ))
{
pointer_to_new_head = &(*pointer_to_new_head)->next;
}
copy_of_head->next = *pointer_to_new_head;
*pointer_to_new_head = copy_of_head;
}
return new_head;
}
可能很多人認為指針是一個糟糕的特性, 也有很多人認為掌握指針的各種技巧在C++11 里各種封裝好的smart pointer
前只是班門弄斧. 但是我想說, 世界上不存在神這種玩意, 所以永遠不要把一個人當做神, 即使他/她再強, 說的話也不應(yīng)該全盤被信任, 所以你必須做出自己的判斷, 這判斷不是為了判斷而判斷, 而是必須盡快的切換到學(xué)習(xí)知識而不是犯傻逼腦殘的所謂選擇困難癥的狀態(tài), 否則只會在一票大神面前永遠迷失方向, 今天某大v說學(xué)這個是沒有意義的, 明天另一個大v說學(xué)那個是有意義的. 如果你信任所有人, 就會陷入什么都想學(xué), 卻什么都沒心思學(xué)的困境, 解決問題的唯一途徑就是憑著自己的興趣走下去, 何必管邊上的人所說的話?
等這些天手邊的事忙完后我在再寫一些 double pointers的其它應(yīng)用, 比如鏈表的其他操作, 樹的各種......
cbegin和cend等是在c++14開始添加的, 並非c++11
TL;DR, 是爲了更方便的獲得常量迭代器.
在c++14以前, 你像要得到常量迭代器, 方法比較繁瑣:
typedef vector<MyType> vect;
typedef vect::const_iterator c_iter;
vect v;
// alternatives:
c_iter it = const_cast<vect const &>(v).begin(); // 1
c_iter it = static_cast<c_iter>( v.begin() ); // 2 (explicit)
c_iter it = v.begin(); // 2 (implicit)
雖然現(xiàn)在const有被濫用的趨勢, 但是該用的時候還是要用, 比如fold, 或者其在c++中的等價函數(shù), std::accumulate, 不需要懟迭代器指向的成員進行修改, 此時const-qualify有其合理性.
再者, 其實現(xiàn)也非常簡單, 利用現(xiàn)有設(shè)施很容易, 不會破壞core laungage:
const_iterator cbegin() const;
const_iterator cend () const;
最後, 它與c++0x的其它特性, 比如auto/decltype可以相互配合, 威力成倍增強, 比如如果沒有cbegin, auto只能得到non-const版本.
其實這種問題最好的解決方法就是去 wg21 看其在2004年的提案, http://www.open-std.org/jtc1/...
更新:
窩跟你說...這裏有人在c++14出臺之前抱怨爲什麼沒有const 版本的std::begin()
抱怨, 然後草藥大叔親自登場了...
多線程操縱map?
videojs
應(yīng)該是框架規(guī)則中要加載的類 (AuthorizedAccessTokenController
),跟實際代碼中期望被加載的類名不匹配。
命名空間
下,類名是否有誤今天把Vue的代碼和jQuery的代碼分開成兩個<script>,好像好了
在form上加上onsubmit="return false;"解決了,原來這個報錯不是
$.ajax({
type: "POST",。。。。
導(dǎo)致的
使用線程執(zhí)行 demux_thread 任務(wù)時,主線程要等待它返回(可用 SDL_WaitThread()
),否則 main() 函數(shù)返回時將強制結(jié)束其他線程。
undefined
是php沒有接收到POST數(shù)據(jù),但是你頁面上已經(jīng)提交了數(shù)據(jù),這時候注意檢查你的“請求頭”信息是否正確,
找到了,toFixed()會自動四舍五入...
因為Demo.h中定義的全局變量demo
可以不被初始化。
靜態(tài)存儲期的非局部變量的初始化在程序啟動時進行,通常在main函數(shù)執(zhí)行前完成,除非被推遲了。它們的初始化分成兩類,靜態(tài)初始化和動態(tài)初始化。其中動態(tài)初始化可能會被推遲,推遲與否取決于編譯器實現(xiàn)。而被推遲的動態(tài)初始化可能不會被執(zhí)行。這里demo
的初始化屬于動態(tài)初始化,所以不同編譯器可以產(chǎn)生不同的結(jié)果。Deferred dynamic initialization
最佳實踐是避免程序依賴全局變量構(gòu)造函數(shù)的副作用。而在真的需要這樣一個可以全局訪問的對象時,請用單例模式。
epoll不是“GCC的類庫”,它是Linux專有API,在Mac上無論你用什么編譯器都一樣用不了的。
Mac和FreeBSD有一個類似的功能叫kqueue。
我是寫 Java
的,那么如果要用不那么 OO
的方式(我們一般喜歡用日志 slf4j
之類的),那么我會用下面兩種思路:
public static void printTimes(Object obj, int times) {
String content = obj.toString();
// 通過新建一個異常來獲取調(diào)用棧信息,不拋出即可。
String where = new Exception().getStackTrace()[1].getClassName();
// 后續(xù)省略
}
public interface Printer {
// 此法需要 Java 8
// 聲明默認方法,想要為某個類加上按次數(shù)打印功能時就 implements Printer
default void printTimes(Object obj, int times) {
String where = this.getClass().getSimpleName();
// 后續(xù)省略
}
}
事實上新建異常來獲取調(diào)用棧信息可以拿到非常完整的執(zhí)行環(huán)境信息:
所處類 | 所處方法 | 所在文件名稱 | 所在文件行數(shù)
歡迎討論。
你不是輸入0 0 0了嗎
ISO C 標準中寫到
6.3.1.3 Signed and unsigned integersWhen a value with integer type is converted to another integer type
other than _Bool, if the value can be represented by the new type, it
is unchanged. Otherwise, if the new type is unsigned, the value is
converted by repeatedly adding or subtracting one more than the
maximum value that can be represented in the new type until the value
is in the range of the new type. Otherwise, the new type is signed and
the value cannot be represented in it; either the result is
implementation-defined or an implementation-defined signal is raised.
換句話說,unsigned char 的表示范圍是 [0, 255],不能表示 -1,于是將 -1 加上 256 得到 255。
如果是把 signed char 型 -1 轉(zhuǎn)成 unsigned int,則用 -1 加上 4294967296 得到 4294967295。
對硬件來說,從有符號到無符號的轉(zhuǎn)換就是簡單地在前面補符號位或者直接截斷。
你的程序表明你開的是局部變量而不是全局變量(關(guān)于全局和局部變量你可以參考C++ 全局變量、局部變量、靜態(tài)全局變量、靜態(tài)局部變量的區(qū)別)。
所以你的數(shù)組是開在棧上的,這就涉及到編譯期限制棧大小的問題。如果你申請這么大的數(shù)組是會stackoverflow的,我記得我原來用devc++寫oj的時候開了一個100000的數(shù)組好像就爆棧了,但是現(xiàn)在換到osx的clion下面好像沒事了...
在一般情況下, 不同平臺默認棧大小如下(僅供參考)
SunOS/Solaris 8172K bytes (Shared Version)
Linux 10240K bytes
Windows 1024K bytes (Release Version)
AIX 65536K bytes
當然你可以修改你的默認棧大?。?
1.SunOS/Solaris系統(tǒng):
limit # 顯示當前用戶的棧大小
unlimit # 將當前用戶的棧大小改為不限制大小
setenv STACKSIZE 32768 #設(shè)置當前用戶的棧大小為 32M bytes
2.Linux系統(tǒng):
ulimit -a #顯示當前用戶的棧大小
ulimit -s 32768 #將當前用戶的棧大小設(shè)置為32M bytes
3.Windows (在編譯過程中的設(shè)置)
在 Visual Studio 開發(fā)環(huán)境中設(shè)置此鏈接器選項
北大青鳥APTECH成立于1999年。依托北京大學(xué)優(yōu)質(zhì)雄厚的教育資源和背景,秉承“教育改變生活”的發(fā)展理念,致力于培養(yǎng)中國IT技能型緊缺人才,是大數(shù)據(jù)專業(yè)的國家
北大青鳥中博軟件學(xué)院創(chuàng)立于2003年,作為華東區(qū)著名互聯(lián)網(wǎng)學(xué)院和江蘇省首批服務(wù)外包人才培訓(xùn)基地,中博成功培育了近30000名軟件工程師走向高薪崗位,合作企業(yè)超4
中公教育集團創(chuàng)建于1999年,經(jīng)過二十年潛心發(fā)展,已由一家北大畢業(yè)生自主創(chuàng)業(yè)的信息技術(shù)與教育服務(wù)機構(gòu),發(fā)展為教育服務(wù)業(yè)的綜合性企業(yè)集團,成為集合面授教學(xué)培訓(xùn)、網(wǎng)
達內(nèi)教育集團成立于2002年,是一家由留學(xué)海歸創(chuàng)辦的高端職業(yè)教育培訓(xùn)機構(gòu),是中國一站式人才培養(yǎng)平臺、一站式人才輸送平臺。2014年4月3日在美國成功上市,融資1
浪潮集團項目經(jīng)理。精通Java與.NET 技術(shù), 熟練的跨平臺面向?qū)ο箝_發(fā)經(jīng)驗,技術(shù)功底深厚。 授課風(fēng)格 授課風(fēng)格清新自然、條理清晰、主次分明、重點難點突出、引人入勝。
曾工作于聯(lián)想擔任系統(tǒng)開發(fā)工程師,曾在博彥科技股份有限公司擔任項目經(jīng)理從事移動互聯(lián)網(wǎng)管理及研發(fā)工作,曾創(chuàng)辦藍懿科技有限責任公司從事總經(jīng)理職務(wù)負責iOS教學(xué)及管理工作。
精通HTML5和CSS3;Javascript及主流js庫,具有快速界面開發(fā)的能力,對瀏覽器兼容性、前端性能優(yōu)化等有深入理解。精通網(wǎng)頁制作和網(wǎng)頁游戲開發(fā)。
具有10 年的Java 企業(yè)應(yīng)用開發(fā)經(jīng)驗。曾經(jīng)歷任德國Software AG 技術(shù)顧問,美國Dachieve 系統(tǒng)架構(gòu)師,美國AngelEngineers Inc. 系統(tǒng)架構(gòu)師。