附录A 程序员工作面试的秘密
对硬件知识一知半解是非常危险的。
硅谷程序员面试
尖端计算机行业最值得称道的事情之一就是选择新雇员加入队伍的不寻常方法。
技术能力是你寻求工作面试时唯一重要的特长。
程序员面试就形成了一种非常独特的风格。
怎样才能检测到链表中存在循环
通常第一种答案:
对访问过的每个元素进行标记,继续遍历这个链表,如果遇到某个已经标记过的元素,说明链表存在循环。
第二个限制:这个链表只位于只读内存区域,无法在元素上做标记。
通常第二种答案:
当访问每个元素时,把它存储在一个数组中。检查每一个后继的元素,看看它是否已经存在于数组中。有时候,一些可怜的程序员会纠缠于如何用散列表来优化数组访问的细节,结果在这一关卡了壳。
第三个限制:奥!内存空间非常有限,无法创建一个足够长度的数组。然而,可以假定如果链表中存在循环,它出现在前N个元素之中。
通常第三种答案(如果程序员能够到达这一步):
设置一个指针,指向链表的头部。在接下来对直到第N个元素的访问中,把N-1个元素依次同指针指向的元素进行比较。然后指针移向第二个元素,把它与后面的N-2个元素进行比较。根据这个方法依次进行比较,如果出现比较相等就说明前N个元素中存在循环,否则如果所有N个元素两两之间进行比较都不相等,说明链表中不存在循环。
第四个限制:奥!不!链表的长度是任意的,而且循环可能出现在任何位置(即使是优秀的候选者也会在这一关碰壁)
最后的答案:
首先,排除一种特殊的情况,就是三个元素的链表中第二个元素的后面是第一个元素。
设置两个指针p1和p2,使p1指向第一个元素,p2指向第三个元素,看它们是否相等。如果相等就属于上述这种特殊情况。如果不等,把p1向后移一个位置,p2向后移两个元素。检验两个指针的值,如果相等,说明两个链表中存在循环。如果不相等,继续按照上述方法进行。如果出现两个指针都为NULL的情况,说明链表中不存在循环。如果链表中存在循环,用这种方法肯定能检验出来,因为其中一个指针肯定能够追上另一个(两个指针具有相同的值),不过这可能要对这个链表经过几次遍历才能检测出来。
/*
**编程挑战
*/
寻找循环
证明上面最后一种方法可以检测链表中可能存在的任何循环。在链表中设置一个循环,演练一下你的代码;把循环变得长一些,继续演练你的代码。重复进行,直到初始条件不满足为止。通过检测,确定链表中不存在循环时,算法可以终止。
提示:编写一个程序,然后依次往外推演。
#include <stdio.h>
#include <stdlib.h>
#define TRUE 1
#define FALSE 0
typedef int ELEMENT_TYPE;
struct NODE{
ELEMENT_TYPE value;
struct NODE *link;
};
int loop_in_link_list( struct NODE *p1, struct NODE *p2 );
int main( void ){
struct NODE node, node2, node3;
struct NODE node4, node5, node6;
node.value = 1;
node.link = &node2;
node2.value = 2;
node2.link = &node3;
node.value = 3;
node.link = &node4;
node4.value = 4;
node4.link = &node5;
node5.value = 5;
node5.link = &node6;
node6.value = 6;
node6.link = &node;
int result = loop_in_link_list( &node, node.link->link );
printf( "result = %d\n", result );
return EXIT_SUCCESS;
}
int loop_in_link_list( struct NODE *p1, struct NODE *p2 ){
/*p1 is the first element, p2 is the third element*/
if( p1 ){
if( !p2 ){ /* p2 is a null pointer */
return FALSE;
} else{ /* p2 is not a null pointer */
if( p1 == p2 ){ /*p1 is equal to p2 indicates a loop is in link list*/
return TRUE;
} else{
p1 = p1->link;
p2 = p2->link->link;
while( p2 && p1 ){
if( p2 == p1 ){
return TRUE;
} else{
p1 = p1->link;
p2 = p2->link->link;
}
}
return FALSE;
}
}
} else{
return FALSE;
}
}
输出:
C语言中不同增值语句的区别何在
考虑下面4条语句
x = x + 1; /*正规形式*/
++x; /*前缀自增*/
x++; /*后缀自增*/
x += 1; /*复合赋值*/
这4条语句的功能是相等的,它们都是把x的值增加1。如果像现在这样不考虑前后的上下文,它们之间并没有什么区别。应试者需要(隐式或显式地)提供适当的上下文环境,以便回答这个问题并找出这4条语句之间的区别。注意,最后一条语句是一种在算法语言中表达“x等于x加上1”的便捷方法。因此,这条语句仅供参考,我们需要寻找的是其余3条语句的独特性质。
但自增和自减操作在所有的硬件系统中的应用之广令人难以置信。
有些程序员则在此处未做深入考虑,忽略了当x不是一个简单的变量而是一个涉及数组的表达式时,像x += 1这样的形式很有用的。
如果你有一个复杂的数组引用,并需要证明同一种下标形式在两种引用中都可以使用,那么
node[i>>31] += -(0x01 << (i & 0x7));
就是你应该采用的方法。优秀的应试者还能够指出左值(定位一个对象的表达式的编译器用语---通常具有一个地址,但它既可能是一个寄存器,也可能是地址或寄存器加上一个位段)只被计算了一次。这一点非常重要,因为下面的语句:
mango[i++] += y;
被当做
mango[i] = mango[i] + y; i++;
而不是
mango[i++] = mango[i++] + y;
最好的那位候选人(他最终获得了这个工作---嗨!Arindam)解释说这些区别与编译器的中间代码有关,例如“++x”表示取x的地址,增加它的内容,然后把值放在寄存器中;“x++”则表示取x的地址,把它的值装入寄存器中,然后增加内存中的x的值。
编译器应该有一个选项,可以产生一个汇编指令列表。你也可以把编译器设置为调试模式,这样也常常可以更容易检查对应的C语言和汇编指令。不要使用优化选项,因为这些语句有可能因为优化而被精简掉。
frotz[--j + i++] += --y;
扩展为功能相同但长度更长的:
--y;
--j;
frotz[j + i] = frotz[j+i] + y;
i++;
教训:不要在一行代码里实现太多的功能。
Kernihan和Plauger所指出的那样,“人人都知道调试比第一次编写代码要难上一倍。所以如果在编写代码时把自己的聪明发挥到极致,那么在调试时又该怎么办呢?”
库函数调用和系统调用区别何在
简明的回答是函数库调用是语言或应用程序的一部分,而系统调用是操作系统的一部分。你要确保弄懂“trap”(自陷)这个关键字的含义。系统调用是在操作系统内核发现一个trap或中断后进行的。这个问题的完整答案如以下要点。
函数库调用 | 系统调用 |
在所有的ANSI C编译器版本中,C函数库是相同的 | 各个操作系统的系统调用是不同的 |
它调用函数库中的一个程序 | 它调用系统内核的服务 |
与用户程序相联系 | 是操作系统的一个进入点 |
在用户地址空间执行 | 在内核地址空间执行 |
它的运行时间属于“用户”时间 | 它的运行时间属于“系统”时间 |
属于过程调用,开销较小 | 需要切换到内核上下文环境然后切换回来,开销较大 |
在C程序库libc中有大约300个程序 | 在UNIX中有大约90个系统调用(MS-DOS中少一些) |
记录于UNIX OS手册的第三节 | 记录于UNIX OS手册的第二节 |
典型的C函数库调用:system、fprintf、malloc | 典型的系统调用:chdir、fork、write、brk |
/*
**编程挑战
*/
警告:这个编程挑战对于有些读者可能过于艰巨。
为下列各个问题编写程序。
1.读取一个字符串,并输出它里面字符的所有组合。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ARR_SIZE 20
/*正确的解答,没有重复的组合*/
/*
例如:abc,它的所有字符组合为a,b,c,ab,ac,bc,abc
对于这种类型的题,想到的第一思路就是采用递归进行求解。
首先我们申请一个与所求字符串一样大小的字符数组s,用于保存各个字符的组合。
对于abc这样字符串的进行递归实现:a,ab,abc,ac,b,bc,c
*/
/* 参考链接:https://blog.csdn.net/sanmao0816/article/details/45011597 */
int Recursion( char *str, char *s, int len, int m, int n );
int main( void ){
int len, i, j, m;
char s[6]={0};
char str[] = "12345";
len = strlen( str );
Recursion( str, s, len, 0, 0 );
return EXIT_SUCCESS;
}
int Recursion( char *str, char *s, int len, int m, int n ){
int i;
for(i = n; i < len; i++){
if(i > n){ //当i>n说明,递归结束
m--;
}
s[m] = str[i];
s[++m] = '\0';
printf("%s ",s);
if( i < len - 1 ){
Recursion( str, s, len, m, i+1 );
/*printf( "str = %s, s = %s, len = %d, m = %d, i + 1 = %d\n", str, s, len, m, i + 1 );*/
}
}
}
输出:
参考链接的答案我看不明白,不过答案是正确的。如果你明白,可以在私信告知我!
2.“八皇后”问题(假设棋盘上有8个皇后,要求打印所有使8个皇后不会相互攻击的棋子配置)。 参考链接:八皇后问题
#include <stdio.h>
int main(){
int queen[8] = {0}; //用来储存皇后的位置 即queen的值就为第i行的列
//queen[0]表示第0行
//queen[i]表示第i行
int cnt = 0; //表示摆放了几个皇后,也表示摆放皇后的行数。
int col = 0; //表示在这一列上摆放了皇后
int sum = 0; //总共有几种摆法
while(1){
//在(cnt,col)这个坐标摆放皇后
if(cnt == 1 && queen[0] == 7 && col == 6){
//表示第一行的皇后已经到了第八列且第二行的皇后到了第六列位置,已经摆放不下皇后了就退出循环
break;
}
int isAttack = 0; //用来表示皇后们之间是否能够攻击的到,如果攻击的到就是1,否则就为0
int i=0;
for(i=0;i<cnt;i++){
if(queen[i] == col){ //表示在同一列上
isAttack = 1;
}
int div_row = cnt - i; //表示斜线上的纵坐标之差
int div_col = queen[i] - col; //表示斜线上横坐标之差
if(div_row == div_col ||div_row == -div_col){ //表示在同一斜线上
isAttack = 1;
}
}
if(isAttack == 0){ //表示可以放置
queen[cnt] = col; //记录皇后当前的列数
cnt++; //开始摆放下一个皇后
col = 0; //下一个皇后从第一列开始遍历
if(cnt == 8){ //如果摆满了八个皇后就打印出他们的摆法
for(i=0;i<8;i++){
printf("%d ",queen[i]+1);
}
printf("\n");
sum++; //并且摆放种数+1
do{ //越界问题 //回朔
cnt--; //撤回正在摆放的皇后
col = queen[cnt]+1; //往下一个列寻找摆放位置
}while(col>=8);
}
}else{ //表示不能摆放
col++;
while(col>=8){ //回朔
cnt--; //退一格
col = queen[cnt]+1; //上一个皇后往后移一格
}
}
}
printf("总共有%d种摆法\n",sum);
return 0;
}
输出:
3.给定一个数N,要求列出所有不大于N的素数。
#include <stdio.h>
#include <stdlib.h>
#define N 1000
int main( void ){
int n;
n = N;
while( n >= 2 ){
int i;
for( i = 2; i * i <= n; ++i ){
if( n % i == 0 ){
break;
}
}
if( i * i > n ){
printf( "%d ", n );
}
--n;
}
return EXIT_SUCCESS;
}
输出:
4.编写一个子程序,进行两个任意大小的矩阵乘法运算。
/*
**将两个矩阵相乘。
*/
#include <stdio.h>
#include <stdlib.h>
void matrix_multiply( int *m1, int *m2, int *r, int x, int y, int z );
void print_matrix( int *m, int row, int column );
int main( void ){
int a[3][2] = { { 2, -6 }, { 3, 5 }, { 1, -1 } };
int b[2][4] = { { 4, -2, -4, -5 }, { -7, -3, 6, 7 } };
int c[3][4];
matrix_multiply( &a[0][0], &b[0][0], &c[0][0], 3, 2, 4 );
print_matrix( &a[0][0], 3, 2 );
printf( "\n" );
print_matrix( &b[0][0], 2, 4 );
printf( "\n" );
print_matrix( &c[0][0], 3, 4 );
return EXIT_SUCCESS;
}
void matrix_multiply( int *m1, int *m2, int *r, int x, int y, int z ){
register int *m1p;
register int *m2p;
register int k;
int row;
int column;
/*
**外层的两个循环逐个产生结果矩阵的元素。由于这是按照存在顺序进行的,
**因此可以通过对r进行间接访问来访问这些元素。
*/
for( row = 0; row < x; row += 1 ){
for( column = 0; column < z; column += 1 ){
/*
**计算结果的一个值。这是通过获得指向m1和m2的合适元素的指针,
**在进行循环时,使它们前进来实现的。
*/
m1p = m1 + row * y;
m2p = m2 + column;
*r = 0;
for( k = 0; k < y; k += 1 ){
*r += *m1p * *m2p;
m1p += 1;
m2p += z;
}
/*
**r前进一步,指向下一个元素。
*/
r++;
}
}
}
void print_matrix( int *m, int row, int column ){
int i, j;
for( i = 0; i < row; ++i ){
for( j = 0; j < column; ++j ){
printf( "%4d", *m++ );
}
printf( "\n" );
}
}
输出:
库函数调用通常比行内展开的代码慢,因为它需要付出函数调用的开销。但系统调用比库函数调用还要慢很多,因为它需要把上下文环境切换到内核模式。纯粹从性能上考虑,你应该尽可能地减少系统调用的数量。但是,你必须记住,许多C函数库中的程序通过系统调用来实现功能。最后,那些相信麦田怪圈的人会对“system()函数实际上是一个库函数”这个概念感到困惑。
文件描述符与文件指针有何不同
所有操纵文件的UNIX程序或者使用文件指针或者使用文件描述符来标识它们正在操作的文件。它们是什么?什么时候应该使用?事实上答案非常直截了当,它取决于你对UNIX I/O的熟悉程度以及对各种因素利弊的权衡。
所有操作文件的系统调用都接受一个文件描述符作为参数,或者把它作为返回值返回。“文件描述符”这个名字多少显得有点命名不当。
在SunOS的编译器中,文件描述符是一个小整数(通常为0~255),用于索引开放文件的每个进程表(per-process table-of-open-files)。系统I/O调用有creat(), open(), read(), write(), close(), ioctl()等,但它们不是ANSI C的一部分,不会存在于非UNIX环境中。如果使用了它们,那么你的程序将失去可移植性。因此,建立一组标准I/O库调用是非常有必要的,ANSI C现在规定所有的编译环境都必须支持它们。
为了确保程序的可移植性,应该使用标准I/O库调用,如fopen(), fclose()、putc()、fseek()等---它们中的绝大多数名字中带有一个f。这些调用都接受一个类型为指向FILE结构的指针(有时称为流指针)的参数。FILE指针指向一个流结构,它在<stdio.h>中定义。
结构的内容根据不同的编译器有所不同,在UNIX中通常是开放文件的每个进程表的一个条目。在典型情况下,它包含了流缓冲区、所有用于提示缓冲区有多少字节是实际文件数据的变量以及提示流状态的标志(如ERROR和EOF)等。
*所以,文件描述符就是开放文件的每个进程表的一个偏移量。它用于UNIX系统调用中,用于表示文件。
*FILE指针保存一个FILE结构的地址。FILE结构用于表示开放的I/O流(如hex20938)。它用于ANSI C标准I/O库调用中,用于标识文件。
C库函数fdopen()可以用于创建一个新的FILE结构,并把它与一个确定的文件描述符关联(可以有效地在文件描述符小整数和对应的流指针间进行转换,虽然它并不在开放文件表中产生一个额外的新条目)。
编写一些代码,确定一个变量是有符号数还是无符号数
要回答这个问题,你必须在特定的编译器中确定一个给定的类型是有符号数还是无符号数。在ANSI C中,char类型既可以是有符号数,也可以是无符号数,这是由编译器决定的。当你编写代码需要移植到多个平台时,知道类型是不是有符号数就非常有用了。如果该类型在所有的编译器上编译时都是恒定的,那就再理想不过了。
你无法用函数实现目的。函数形式参数的类型是在函数内部定义的,所以它无法穿越调用这一关。因此,必须编写一个宏,根据参数的声明对它进行处理。
接下来就是区别宏的参数到底是一个类型还是一个类型的值。假定参数是一个值,无符号的数的本质特征是它永远不会是负的,有符号数的本质特征是对最左边一个位取补将会改变它的符号(比如2的补码,它肯定是个负数)。由于作为参数的这个值的其他位与这个测试无关,因此对它们全部取补后结果是一样的。因此,可以像下面这样尝试:
#define ISUNSIGNED(a) (a >= 0 && ~a >= 0)
如果宏的参数是一个类型,其中一个方法是使用类型转换:
#define ISUNSIGNED(type) ((type)0 - 1 > 0)
面试的关键就在于正确理解问题。第一个代码只适用于K&R C,新的类型提升规则导致它无法适用于ANSI C。
/*
** 确定变量是有符号数还是无符号数。
*/
#include <stdio.h>
#include <stdlib.h>
#define ISUNSIGNED(type) ((type)0 - 1 > 0 )
#define ISUNSIGNED_2(a) ((a) >= 0 && ~(a) >= 0 )
int main( void ){
int unsigned_value;
unsigned_value = ISUNSIGNED( char );
printf( "unsigned_value = %d\n", unsigned_value );
unsigned_value = ISUNSIGNED_2( 2 );
printf( "unsigned_value = %d\n", unsigned_value );
return EXIT_SUCCESS;
}
输出:
打印一棵二叉树的值的时间复杂度是多少
现在,关于复杂度理论首先需要知道的是大O表示法。O(N)表示当N(通常是需要处理的对象数量)增长时,处理时间几乎是按照线性增长的。关于复杂度理论其次需要知道的是在一棵二叉树中,所有操作的时间复杂度都是O(log(n))。所以,很多程序员不假思索地作出了这个回答。错误!
这个问题有点类似于Dan Rather著名的“频率是什么,Kenneth?”问题---这个问题用于干扰、混淆和激怒对方而不是真的向对方咨询信息。要打印一棵二叉树所有结点的值,必须对它们逐个访问,所以时间复杂度为O(N)。
/* tree.h头文件的内容如下:*/
#ifndef TREE_H
#define TREE_H
#define TREE_TYPE int
typedef struct TREE_NODE{
TREE_TYPE value;
struct TREE_NODE *left;
struct TREE_NODE *right;
} TreeNode;
/*
** 添加值到树中。
*/
void insert( TREE_TYPE value );
/*
** 打印当前节点的值。
*/
void print_node( TREE_TYPE value );
/*
** 前序遍历树的值。
*/
void pre_order_traverse( void (*callback)( TREE_TYPE value ) );
/*
** 释放树中的内存。
*/
void destroy_tree( void );
#endif
/*
** tree.c
** 树的实现。
*/
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "tree.h"
/*
**指向树根节点的指针。
*/
static TreeNode *tree;
/*
**insert
**添加值到树中。
*/
void insert( TREE_TYPE value ){
TreeNode *current;
TreeNode **link;
/*
**从根节点开始。
*/
link = &tree;
/*
**持续查找值,进入合适的子树。
*/
while( (current = *link) != NULL ){
/*
**根据情况,进入左子树或右子树(确认没有出现重复的值)。
*/
if( value < current->value ){
link = ¤t->left;
}else{
assert( value != current->value );
link = ¤t->right;
}
}
/*
**分配一个新节点,使适当节点的link字段指向它。
*/
current = (TreeNode *)malloc( sizeof( TreeNode ) );
assert( current != NULL );
current->value = value;
current->left = NULL;
current->right = NULL;
*link = current;
}
/*
**print_node
**打印当前节点的值。
*/
void print_node( TREE_TYPE value ){
printf( "%d ", value );
}
/*
**do_pre_order_traverse
**执行一层前序遍历。这个帮助函数用于保存当前正在处理的节点的信息。
**这个函数并不是用户接口的一部分。
*/
static void do_pre_order_traverse( TreeNode *current, void (*callback)( TREE_TYPE value ) ){
if( current != NULL ){
callback( current->value );
do_pre_order_traverse( current->left, callback );
do_pre_order_traverse( current->right, callback );
}
}
/*
**pre_order_traverse
**前序遍历树中的值。
*/
void pre_order_traverse( void (*callback)( TREE_TYPE value ) ){
do_pre_order_traverse( tree, callback );
}
/*
**destroy_node
**释放树中占据的一个节点的内存。
**这个函数并不是用户接口的一部分。
*/
static void destroy_node( TreeNode *node ){
TreeNode *left_node;
TreeNode *right_node;
TREE_TYPE value;
if( node ){
left_node = node->left;
right_node = node->right;
if( left_node ){
destroy_node( left_node );
}
if( right_node ){
destroy_node( right_node );
}
value = node->value;
printf( "%d ", value );
free( node );
}
}
/*
**destroy_tree
**释放树中占据的内存。
*/
void destroy_tree( void ){
destroy_node( tree );
}
/*
** tree_test.c
** 打印一棵二叉树的值。
*/
#include <stdio.h>
#include <stdlib.h>
#include "tree.h"
int main( void ){
TREE_TYPE values[] = {1, 2, 3, 4, 5, 6, 7, 8 };
int i;
int size;
size = sizeof(values) / sizeof(*values);
for( i = 0; i < size; ++i ){
insert( values[i] );
}
printf( "print the values of elements in the tree:\n" );
pre_order_traverse( print_node );
printf( "\n" );
destroy_tree();
return EXIT_SUCCESS;
}
输出:
从文件中随机提取一个字符串
解决这个问题的经典方法是读取文件,然字符串进行计数,并记录每个字符串的偏移位置。然后,在1和字符串总数之间取一个随机数,根据选中字符串的偏移位置取出该字符串。
主考官设置了一个条件。他要求只能按顺序遍历文件一次,并且不能使用表格来存储所有字符串的偏移位置。对于这个问题,主考官的主要兴趣在于你如何解决问题的过程。如果你提问,他会给你一些提示,所以大多数面试者最终都能获得答案。主考官对你的满意程度取决于你获得答案的速度。
基本的技巧是在幸存的字符串中挑选,并在过程中不断更新。从计算的角度看,这个方法是非常低效的,所以它很容易被忽略。你打开文件并保存第一个字符串,此时就有了一个备选字符串,并有可能100%的可能性选中它。保存这个字符串,继续读入下一个字符串,这样就有了两个备选字符串,选中每个的可能性都是50%。选中其一并保存,然后丢弃另一个。再读入下一个字符串,按照新字符串33%的概率和原先幸存的字符串67%的概率(它代表前两个字符串的幸存者),在两者之间选择一个,然后保存新选中的字符串。根据这个方法,依次对整个文件进行处理。在其中每一步,读入字符串N,在它(按照1/N的概率)和前一个幸存的字符串(按照(N-1)/N的概率)之间进行选择。当到达文件末尾的时候,最后一个幸存的字符串就是从整个文件中随机提取的那个字符串!
/*
** 从文件中随机提取一个字符串。
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#define random(x) (rand() % x) //产生x内的随机函数
#define RAND_N 1000
//自定义随机器
void my_random(char *buf1, char *buf2, int count)
{
//判断范围
if(random(RAND_N) < RAND_N / count)
{
strcpy(buf1, buf2);
}
}
//主函数
int main()
{
FILE *fp;
int count = 0;
char buf1[100], buf2[100];
if ((fp = fopen("d:/test.txt", "r")) == NULL) {
fprintf(stderr, "open error");
exit(0);
}
if(fscanf(fp, "%s", buf1) == EOF)
{
printf("file is null\n");
return 0;
}
count++;
srand((int)time(0));//设置随机数种子,srand不能调用两次以上
for(count++; fscanf(fp, "%s", buf2) != EOF; count++)
{
my_random(buf1, buf2, count);
}
fclose(fp);
printf("随即读取的字符串为 : %s\n", buf1);
return 0;
}
参考链接:https://blog.csdn.net/ty616114553/article/details/6799789
输出:
这是一个非常艰难的问题,你要么依靠可能少的提示获得答案,要么就预先做好充分准备,提前阅读这本书。
更多阅读材料
如果你喜欢这本书,你可能也会喜欢Bartholomev and the Oobleck,作者是Seuss博士(纽约,Random Houese, 1973)。
软件工程师如果细心阅读Bartholomev and the Oobleck,肯定能从中获益。
如果每位程序员只是偶尔玩弄Oobleck,这个世界则会美好许多。
事实就是如此。人类的最高目标是奋斗、寻求、创造。每位程序员应该寻找并抓住每一次机会,使自己...哇!写的太多了。
附录A 程序员工作面试的秘密相关推荐
- C/C++程序员工作面试的秘密
稍微懂些硬件知识是非常危险的.一位程序员把一张新奇的能演奏颂歌的圣诞卡片拆了开来,取出其中的压电乐曲芯片.他偷偷地把它安装在老板的键盘上,并连接到一个发光二极管上.他进行了测试,一个能够点亮发光二极管 ...
- 一个中科大差生的 8 年程序员工作总结
关注.星标公众号,直达精彩内容 之前分享过一些大佬的程序人生,不少读者留言说喜欢看这类文章,因为多多少少都会对自己有一定的启发,而且也快过春节了,相信大家也没有心思看技术文章了,哈哈. 这个星期我又发 ...
- 一个毕业6年的程序员工作经历和成长感悟(上)
把时钟拨回到2007年的夏天,大学毕业.那时非常迷茫,不知道自己能做什么,想做什么,对工作有一种期待和憧憬,只是觉得计算机.网络有关的职位都可以投递简历. 2007年5月12日(历史惊人的巧合,没想到 ...
- 一个毕业6年的程序员工作经历和成长感悟(中)
接上篇:一个毕业6年的程序员工作经历和成长感悟(上) 2009年6月,入职新公司.(因为我依旧在公司就职,就不透露公司名了,直接用"公司"二字表示,下文中涉及到开发的项目也会分别用 ...
- 程序员工作经验谈之商贸平台
程序员工作经验谈 1.怎样顺利找到工作,并稳稳当当坐下来呢? ①如何通过面试? 答:态度要积极,要听话.积极并且有热情. 表达能力要良好,能够流利介绍个人情况,例如工作情况,为何离职等. 专业基础要扎 ...
- 毕业一年的大专生程序员工作总结(java后台)
文章导读 一.回眸过去 – 闲扯的话 – 零碎的技术 二.经验总结 – 沟通交流 – 贵在坚持 – 合理规划 三.展望未来 – 积累行业背景 – 学习清单 四.最后补充 一. 回牟过去 1.闲扯的话 ...
- 转载-一个中科大差生的8年程序员工作总结 - 陈小房的文章 - 知乎
作者:陈小房 链接:https://zhuanlan.zhihu.com/p/343098771 来源:知乎 著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 今年终于从大菊花厂离 ...
- 程序员工作很轻松,一起来看看
每次朋友听说我们是程序员时都很羡慕,觉得我们拿着高工资工作很轻松,至少不要风吹日晒. 错了,我们来看看程序员工作到底有多累.如果不是这个情况,说明你超过了99%的程序员. 刚入行的时候,代码水平不高, ...
- 程序员工作不稳定?你以为的稳定工作,其实都是高风险职业
你一定听到过别人这样的议论: "程序员工作太不稳定,天天总跳槽,而且年龄越大越不吃香...." 今天笔者来带大家算一笔账,看看他们口中稳定的职业,和"程序员"不 ...
最新文章
- paper 38 :entropy
- Java黑皮书课后题第5章:**5.22(金融应用:显示分期还贷时间表)对于给定的贷款额,月支付额包括偿还本金及利息。编写一个程序,让用户输入贷款总额年限利率,然后显示分期还贷时间表
- Web开发-Django模型层
- 利用数据的商业智能分析工具
- ORA-39070:无法打开日志文件
- 成年男女间存在真正的友谊吗?
- 大数据技术落地需要注意哪些问题
- React Native 触摸事件处理详解
- 业务异常通用类及全局异常处理
- LInux下Docker 傻瓜式安装一步到位
- 皮克定理和任意多边形的面积公式
- 撩小姐姐的小程序(二)----旋转3D八音盒
- 以下名字 不能作为c语言标识符的是,(完整版)《C语言程序设计》复习参考答案...
- 获得lazada商品详情
- 小班安全使用计算机教案,小班安全教案《使用学习用具》
- 泛函分析笔记(二)选择公理和佐恩引理
- 使用jQuery实现电影排行榜
- ANSYS Workbench 14.5数值模拟工程实例解析视频教程
- React Native项目配置路由和选项卡导航__React Navigation的使用
- HDOJ题目1290献给杭电五十周年校庆的礼物(数学,递推)