- 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
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
enum {
HOST = INADDR_LOOPBACK,
PORT = 6666,
MAX_BUF = 1024
};
struct sock {
int sockfd;
int addrlen;
struct sockaddr_in addr;
} host, client;
int check(int x, char*msg)
{
if (!~x) {
perror(msg);
exit(1);
}
return x;
}
#define QUOTE_(...) #__VA_ARGS__
#define QUOTE(...) QUOTE_(__VA_ARGS__)
#define CHECK(...) check(__VA_ARGS__, QUOTE(line __LINE__: __VA_ARGS__))
int main(int argc, char**argv)
{
struct sock host, client;
host.sockfd = CHECK(socket(AF_INET, SOCK_STREAM, 0)),
host.addr = (struct sockaddr_in){AF_INET, htons(PORT), htonl(HOST)};
CHECK(bind(host.sockfd, &(struct sockaddr)host.addr, sizeof(host.addr)));
CHECK(listen(host.sockfd, 1));
CHECK(client.sockfd = accept(host.sockfd, (void*)&client.addr, &client.addrlen));
printf("connected: %s\n", inet_ntoa(client.addr.sin_addr));
struct {int len; char buf[MAX_BUF];} msg;
while (CHECK(msg.len = recv(client.sockfd, msg.buf, MAX_BUF - 1, 0)) && msg.len) {
msg.buf[msg.len] = 0;
printf("%s", msg.buf);
send(client.sockfd, msg.buf, msg.len, 0);
}
close(client.sockfd);
close(host.sockfd);
return 0;
}
потому что сокет может быть и не интернетный
В нем char sa_data[14];
А вот в sockaddr_in эти 14 байт расложили так
u_short sin_port; // на порт 2 байта
struct in_addr sin_addr; //на адрес 4 байта
char sin_zero[8]; // на добивку
Переменные с теми же самыми именами объявлены в функции main (что обычно кладётся в стек):
Если убрать локальное объявление (ту самую 36-ю строку), то у нидлеса программа не может определить адрес клиента.
И правильно делает! Ибо accept'у некуда писать этот адрес. В отличие от случая с локалкой где всё случайно работает из-за UB'а.
если int pitux
то ptiux == 0?
В глобалке - ноль. В локалке - UB.
Инициализируйте переменные, блеать.
The addrlen argument is a value-result argument: the caller must initialize it to contain the size (in bytes) of the structure pointed to by addr; on return it will contain the actual size of the peer address.
А мог бы и случиться адрес 169.253.0.0
The returned address is truncated if the buffer provided is too small; in this case, addrlen will return a value greater than was supplied to the call.
Х.з., сомнительный ворнинг так то (у студии есть, емнип). Из хедеров много всякого говна торчит, которое ты никогда юзать не будешь. Запрещать все эти имена юзать в качестве локалок, имхо, оверкилл.
А вот в пределах одной функции можно было бы и показать, это на 99% залёт. Но тоже нет.
В обоих случаях результат зависит от деталей реализации.
1. Глобальные переменные.
У секции .data есть инициализированная часть, лежащая в экзешнике, которая забита нулями и прочими начальными значениями (константами и правой частью от выражений типа int x = 42;), и неинициализированная часть, которая создаётся после загрузки (чем она забита, решает загрузчик экзешника).
Кудкудкудкуда ляжет переменная (в инициализированную часть или в неинициализированную), решают кококонпелятор и линкер.
2. Локальные переменные.
Тут всё хуже. В стек может наложить кто угодно. Я помню, как Царь полагался на то, что его промежуточные данные в стеке между вызовами функций сохраняются, и его программа обломалась в Ideone, где какой-то патч типа Propolice или Stackguard чистит стек вилкой.
Значит, у Нидлеса вариант с локальными переменными работает только потому, что по счастливой случайности кто-то в эту область стека положил ненулевые значения.
6.7.8 Initialization
10) <...> If an object that has static storage duration is not initialized explicitly, then <...> if it has arithmetic type, it is initialized to (positive or unsigned) zero.
А с «неарифметическими» что будет?
- if it has arithmetic type, it is initialized to (positive or unsigned) zero;
- if it is an aggregate, every member is initialized (recursively) according to these rules;
- if it is a union, the first named member is initialized (recursively) according to these rules.
Но это всё касается только static storage duration, на локалки это правило не действует и они наполнены UB'ами.
А если другие члены окажутся длиннее первого, то их хвосты можно не инициализировать?
> если операционка не справляется
Ну это странно... У процесса своей грязной памяти ещё нету, а брать её из других процессов не почистив вилкой - ебучее решето. Хотя какая-нибудь мелкая ось, у которой всё доверенное и без изоляции, в принципе, имеет право.
1. В некоторых системах null pointer может быть не нулём, а специально подготовленным значением (-1 или что-нибудь ещё хуже). Или в сишке теперь он всегда ноль?
2. Плавающий питух имеет множество реализаций.
С IEEE754 повезло: там порядок хранится со смещением (bias), так что порядок, забитый нулями соответствует самому низкому значению порядка (-127 во float, -1023 в double, -16383 в long double), поэтому если забить нулями и мантиссу, и порядок, как раз получим +0, что не противоречит стандарту.
Если питух другой, то для обнуления числа придётся забивать чем-нибудь другим.
Стандарт позволяет использовать не 754?
Накладок не будет т.к. только первый именованный из них обязан инициализироваться.
> использовать не 754
Походу да:
Annex F (normative) IEC 60559 floating-point arithmetic
F.1 Introduction
1) <...> An implementation that defines __STDC_IEC_559__ shall conform to the specifications in this annex.
Другим плавучим питухам просто нельзя объявлять это макро.
1. MBF (Microsoft binary format, BASIC). Порядок со смещением 128, причём для чисел любой точности. Старший разряд мантиссы подразумевается равным единице, как у IEEE 754. Denormal, inf, NaN не поддерживаются. Знаковый бит где-то в середине.
Если все биты обнулены, то хранится +0.
2. HFP (hexadecimal floating point, IBM System/360). Порядок со смещением 64 для чисел любой точности, основание порядка равно 16, а не 2 (поэтому старший подразумеваемый разряд мантиссы равен нулю). Знаковый бит в самом начале.
Если все биты обнулены, то хранится +0.
3. 48-битный Real в Трубопаскакале. Порядок хранится со смещением 128. Мантисса записывается задом наперёд. Знаковый бит где-то в середине. Старший разряд мантиссы подразумевается равным единице, как у IEEE 754.
Если все биты обнулены, то хранится +0.
Найденные питухи легко обнулять. Нужно найти какой-нибудь посложнее.
У БЭСМ порядок хранился без смещения (т. е. нули соответствовали не минимальному значению, а нулевой степени). Но я пока ещё не прочитал, как там хранилась мантисса.
В любом случае, у БЭСМ-6 ноль состоял из нулей. А вот минус нуля (судя по дополнительному коду) не было.
P.S. Какие ещё плавпитухи бывают?
P.P.S. В IEEE 754 описан ещё двоично-десятичный питух. Строится он по тому же принципу, что и двоичный, только бывают ненормальные кодировки порядка для экономии битов.
Потму што память под static выделяется в момент запуска и там всё равно чем ее забить, а стек выделяется каждый раз и нехуй тратить время на ненужное?
6.2.5 Types
18) Integer and floating types are collectively called arithmetic types.
Или её ещё нет или вообще нет, а сишку юзать хочется...
всмысле некуда? Я же передал укозатель на сьруктуру?
З.Ы. Первый вопрос, который должен возникать при вызове незнакомой функции - "а где она берёт длину буфера?"
Я думал она знает размер сьруктуры.
А вслепую вроде только gets (функция, запрещённая к использованию) пишет.
ещё scanf("%s", &s)
sockaddr_in чтоб кастовать меньше
> if (!~x)
Это что за покемон? Хакерская проверка на -1? Ещё не хватало у знакового числа биты инвертировать...
> && msg.len
Недостижимый код, у тебя CHECK и так вернёт 0. while (msg.len = CHECK(recv(...)), имхо, было бы немного наглядней.
> send(client.sockfd, msg.buf, msg.len, 0);
Уверен, что улетит одним куском? Так можно и байты проебать. Ну и CHECK забыл.
начиная с непроверки результтата байнда (всякый адрес олреди ин юз) и заканчивая вообще говоря тем что TCP (STREAM) у нас с recv, что тоже как-то подозрительно (аут оф баунд чилишоли?)
Короче Стивенсом пиздить по голове кмк
> непроверки результтата байнда
Проверен же. Или я слепой?
Островом сокровищ? Или криптономиконом? Второе, наверное, ближе к теме.
Я всё проверил.
> TCP (STREAM) у нас с recv
прочитал на rsdn что read/write не рекомендуется
> аут оф баунд
это с чего вдрукк?
man tcp
А так же прямо из мана
The only difference between recv() and read(2) is the presence of flags.
А про винду ты прав: read на выньсок не раб
http://img0.reactor.cc/pics/post/-4500162.jpeg
но он такой маленький няшка, как его не вставить? ^_^
> Ещё не хватало у знакового числа биты инвертировать
а знаковость как на побитловые опереции влияет?
> Недостижимый код
изначально были проверки на -1 без функции, там нужно было, так и осталось.
> улетит одним куском?
на холокосте да, тестовая программка же.
Стандарта под рукой нет, но у меня ощущение, что там возможен UB.
each bit in the result is set if and only if the corresponding bit in the converted operand is
not set). The integer promotions are performed on the operand, and the result has the
promoted type. If the promoted type is an unsigned type, the expression ~E is equivalent
to the maximum value representable in that type minus E.
Обычная побитовая инверсия.
Педанты могут пройтись по упоминаниям sign bit, trap (representation), negative zero и какие там ещё ужасы. Но !~x того не стоит.
А не стоит - без причин добавлять завязку на особенности реализации.
В мане ведь было: "On error, -1 is returned", а не ~0U. И ничего постыдного нет в очевидной проверке x==-1.
https://en.wikipedia.org/wiki/Fast_inverse_square_root#History_and_inv estigation
1) Дополнительный код (two's complement), когда вся арифметика по модулю 2 в степени N, где N — количество двоичных разрядов в числе.
Тогда -1 = ~0, наибольшее положительное число равно pow(2, n-1) - 1; наименьшее число равно - pow(2, n-1).
Применяется повсеместно для целых чисел.
2) Обратный код (one's complement). Его особенность в том, что -x == ~x, поэтому у него два нуля: +0 и -0 (в представлении последнего все биты установлены, как у числа pow(2, n-1) - 1).
Диапазон допустимых значений симметричный: [ -(pow(2, n-1) - 1); + (pow(2, n-1) - 1)].
Операции между положительными и отрицательными числами в таком представлении вызывают головную боль. К счастью, он применялся только в древних компьютерах, и сейчас о нём можно забыть.
3) Прямой код. У числа есть знаковый бит, за которым следует модуль числа. Проблемы те же, что и у обратного кода: два нуля (+0 и -0), сложность сложения чисел разных знаков. Однако, есть и положительные стороны: легко умножать и делить числа произвольного знака, легко выводить на экран.
В прямом коде обычно хранится мантисса вещественных чисел.
4) Число со смещением. Ко всем числам прибавляется константа, чтобы они стали положительными. В таком формате обычно хранится порядок (показатель, экспонента) вещественных чисел. В этом формате и ноль выглядит не как ноль, а как 1 << (n - 1).
*****
По факту на большинстве современных процессоров для хранения целых используется дополнительный код (two's complement), где -1 == ~0 (вообще -x == ~(x - 1)), но если пишем портируемый код, то полагаться на это нельзя.
Медик?
живых
а крыс жалко, если честно
они няшечки такие
Говно виртуольно-вербозное анально-объектно-ориентированое.