s = pthread_mutex_unlock(&thread_mutex); if (s != 0) errExitEN(s, "pthread_mutex_unlock");
s = pthread_cond_signal(&thread_died); if (s != 0) errExitEN(s, "pthread_cond_signal");
returnNULL; }
intmain(int argc, char *argv[]) { int s, idx;
thread = calloc(argc - 1, sizeof(*thread)); if (thread == NULL) errExit("calloc"); for (idx = 0; idx < argc-1; ++idx) { thread[idx].sleep_time = getInt(argv[idx+1], GN_NONNEG, NULL); thread[idx].state = TS_ALIVE; s = pthread_create(&thread[idx].tid, NULL, thread_func, &idx); if (s != 0) errExitEN(s, "pthread_create"); }
tot_threads = argc - 1; num_live = tot_threads;
while (num_live > 0) { s = pthread_mutex_lock(&thread_mutex); if (s != 0) errExitEN(s, "pthread_mutex_lock");
while (num_unjoined == 0) { s = pthread_cond_wait(&thread_died, &thread_mutex); if (s != 0) errExitEN(s, "pthread_cond_wait"); }
for (idx = 0; idx < tot_threads; ++idx) { if (thread[idx].state == TS_TERMINATED) { s = pthread_join(thread[idx].tid, NULL); if (s != 0) errExitEN(s, "pthread_join");
Any integer can be cast to any pointer type. Except for the null pointer constants such as NULL (which doesn’t need a cast), the result is implementation-defined, may not be correctly aligned, may not point to an object of the referenced type, and may be a trap representation.
Any pointer type can be cast to any integer type. The result is implementation-defined, even for null pointer values (they do not necessarily result in the value zero). If the result cannot be represented in the target type, the behavior is undefined (unsigned integers do not implement modulo arithmetic on a cast from pointer)
#ifdef CONFIG_FAIR_GROUP_SCHED structsched_entity *parent; /* rq on which this entity is (to be) queued: */ structcfs_rq *cfs_rq; /* rq "owned" by this entity/group: */ structcfs_rq *my_q; #endif };
/* * Task state bitmask. NOTE! These bits are also * encoded in fs/proc/array.c: get_task_state(). * * We have two separate sets of flags: task->state * is about runnability, while task->exit_state are * about the task exiting. Confusing, but this way * modifying one set can't modify the other one by * mistake. */ #define TASK_RUNNING 0 #define TASK_INTERRUPTIBLE 1 #define TASK_UNINTERRUPTIBLE 2 #define __TASK_STOPPED 4 #define __TASK_TRACED 8 /* in tsk->exit_state */ #define EXIT_ZOMBIE 16 #define EXIT_DEAD 32 /* in tsk->state again */ #define TASK_DEAD 64 #define TASK_WAKEKILL 128 #define TASK_WAKING 256 #define TASK_STATE_MAX 512
首先,C语言里有整数常量(integer constant),让整型数值直接在表达式中使用。比如说int a = 10这里出现的10,就是个整型常量。整型常量除了默认的十进制,可以在开头加0表示八进制,比如010就是表示的八进制数字10;还可以在开头加0x或者0X表示十六进制,比如说0xFF就表示十六进制数字FF。
A segment selector is a 16-bit identifier for a segment (see Figure 3-6). It does not point directly to the segment,but instead points to the segment descriptor that defines the segment.
A logical address consists of a segment selector and an offset. The segment selector is a unique identifier for a segment. Among other things it provides an offset into a descriptor table (such as the global descriptor table, GDT) to a data structure called a segment descriptor. Each segment has a segment descriptor, which specifies the size of the segment, the access rights and privilege level for the segment, the segment type, and the location of the first byte of the segment in the linear address space (called the base address of the segment). The offset part of the logical address is added to the base address for the segment to locate a byte within the segment. The base address plus the offset thus forms a linear address in the processor’s linear address space.
If a substitution results in an invalid type or expression, type deduction fails. An invalid type or expression is one that would be ill-formed, with a diagnostic required, if written using the substituted arguments. [ Note: If no diagnostic is required, the program is still ill-formed. Access checking is done as part of the substitution process. — end note ] Only invalid types and expressions in the immediate context of the function type and its template parameter types can result in a deduction failure. [ Note: The substitution into types and expressions can result in effects such as the instantiation of class template specializations and/or function template specializations, the generation of implicitly-defined functions, etc. Such effects are not in the “immediate context” and can result in the program being ill-formed. — end note ]
const std::string的右值不能传给rvalue reference to a non-const std::string(即不会调用移动构造函数),却可以传给lvalue-reference-to-const(即会调用拷贝构造函数)。因为移动一个对象会改变它的某些值,所以c–不允许const对象传给可能会改变它们值的函数。
template <typename T> voidf(T&& param); // param is a universal reference
auto 表达式
1
auto&& var2 = val1; // var2 is a universal reference
如果加了const,就会绑定到rvalue reference
1 2
template<typename T> voidf(const T&& param); // param is a rvalue reference
不要在模板里看到T&&就以为是universal reference。比如在vector中:
1 2 3 4 5 6 7 8
template<typename T, classAllocator = allocator<T>> class vector { public: voidpush_back(T&& x); // x is a reference to rvalue
template<class... Args> voidemplace_back(Args&&... args); // args are universial references }
C++14的lambda表达式可以声明 auto&& 参数:
1 2 3 4 5 6 7 8 9
auto timeFuncInvocation = [](auto&& func, auto&&... params) { //start timer; std::forward<decltype(func)>(func)( std::forward<decltype(params)>(params)... ) //stop timer and record elapased time }
// With universial reference classWidget{ public: template<typename T> voidsetName(T&& newName) { name = std::forward<T>(newName); } // ... Other codes
private: std::string name; }
// With two override functions classWidget{ public: voidsetName(const std::string& newName) { name = newName; }
voidsetName(std::string&& newName) { new = newName; } // ... Other codes
private: std::string name; }
前者就比后者好一些。首先,后者增加了源代码的编写以及要维护的代码量。其次,它可能更加低效。比如说:
1
w.setname("cyyzero");
使用universal reference版本,那么它被转发给std::string,并且只调用 std::string 的一次赋值函数。而对于两个重载版本的setName,将会创建一个临时的std::string对象,然后临时对象移动,最后析构。Last but not least,对于后者不仅源代码体积膨胀和执行期的效率低下,而且它是一种可拓展性很差的设计。可能需要重载的数量会很多。