Массив заполняется числами -858993460 [закрыт]
Закрыт. Этот вопрос не по теме. Ответы на него в данный момент не принимаются.
Закрыт 7 лет назад .
- Вопрос вызван проблемой, которая больше не воспроизводится, или опечаткой. Хотя похожие вопросы могут быть уместны на этом сайте, решение для этого вопроса вряд ли поможет будущим посетителям. Обычно можно избежать подобных вопросов написанием и исследованием минимальной программы для воспроизведения проблемы до публикации вопроса.
- Вопросы с просьбами помочь с отладкой («почему этот код не работает?») должны включать желаемое поведение, конкретную проблему или ошибку и минимальный код для её воспроизведения прямо в вопросе. Вопросы без явного описания проблемы бесполезны для остальных посетителей. См. Как создать минимальный, самодостаточный и воспроизводимый пример.
Задача состояла в том, что бы в каждой строке матрицы найти произведение чисел между максимальным и минимальным членами, а если нельзя высчитать (рядом стоят) — то вывести 0.
#include "stdafx.h" #include "math.h" #include "iostream" using namespace std; int main() < int k, l; int mas[12][14]; cout > l; l = 2; cout > k; k = 2; //+++++++++Ввод эл массива for (int i = 0; i < k; i++) < for (int j = 0; j < l; j++) < cin >> mas[k][l]; > > //OOOOOOOOOOOOOOOOOOOOOOOO for (int i = 0; i < k; i++)/1 итерация на 1ну строку на экране int min = mas[i][1]; //мин и макс элементы int max = mas[i][1]; // int minN = 1; //Номера мин и int maxN = 1; //макс элементов int proiz = 1; for (int j = 0; j < l; j++) < //сканирование на минимальные члены в строке(в l) if (min > mas[i][j]) < min = mas[i][j]; minN = j; >if (max < mas[i][j]) < max = mas[i][j]; maxN = j; >> for (int g = 0; g < l; g++) < cout //вывод строки cout 1) < //условие, при которм произведение существует if (minN < maxN) < for (int y = minN+1; y < maxN; y++) < //вычисление произведения proiz *= mas[i][y];>> else for (int y = maxN+1; y < minN; y++) < proiz *= mas[i][y]; >//тоже вычисление произведения > else < proiz = 0; >//0 cout return 0; >
C 858993460 что это означает
БлогNot. Лекции по C/C++: указатели и строки Си
Лекции по C/C++: указатели и строки Си
Указатель — это переменная, содержащая адрес некоторого объекта в оперативной памяти (ОП). Смысл применения указателей — косвенная адресация объектов в ОП, позволяющая динамически менять логику программы и управлять распределением ОП.
- работа с массивами и строками;
- прямой доступ к ОП;
- работа с динамическими объектами, под которые выделяется ОП.
Описание указателя имеет следующий общий вид:
тип *имя;
то есть, указатель всегда адресует определённый тип объектов! Например,
int *px; // указатель на целочисленные данные char *s; //указатель на тип char (строку Си)
Опишем основные операции и действия, которые разрешены с указателями:
1. Сложение/вычитание с числом:
px++; //переставить указатель px на sizeof(int) байт вперед s--; //перейти к предыдущему символу строки //(на sizeof(char) байт, необязательно один)
2. Указателю можно присваивать адрес объекта унарной операцией » & «:
int *px; int x,y; px=&x; //теперь px показывает на ячейку памяти со // значением x px=&y; //а теперь – на ячейку со значением y
3. Значение переменной, на которую показывает указатель, берется унарной операцией » * » («взять значение»):
x=*px; //косвенно выполнили присваивание x=y (*px)++; //косвенно увеличили значение y на 1
Важно! Из-за приоритетов и ассоциативности операций C++ действие
имеет совсем другой смысл, чем предыдущее. Оно означает «взять значение y ( *px ) и затем перейти к следующей ячейке памяти ( ++ )»
Если px по-прежнему показывал на y , он означает «записать значение y в x и затем перейти к ячейке памяти, следующей за px «. Именно такой подход в классическом Си используется для сканирования массивов и строк.
Вот пример, с точностью до адресов памяти показывающий это важное различие. Комментарием приведены значения и адреса памяти переменных x и y , а также значение, полученное по указателю px и адрес памяти, на который он показывает. Обратите внимание, что после выполнения второго варианта кода значение, полученное по указателю, стало «мусором», так как он показывал на переменную, а не на нулевой элемент массива.
#include int main() < int x=0,y=1; int *px=&y; printf ("\nx=%d on &%p, y=%d on &%p, *px=%d on &%p",x,&x,y,&y,*px,px); x=(*px)++; //после первого запуска замените на x=*px++; printf ("\nx=%d on &%p, y=%d on &%p, *px=%d on &%p",x,&x,y,&y,*px,px); /* Действие (*px)++ x=0 on &002CFC14, y=1 on &002CFC08, *px=1 on &002CFC08 x=1 on &002CFC14, y=2 on &002CFC08, *px=2 on &002CFC08 Действие *px++ x=0 on &0021F774, y=1 on &0021F768, *px=1 on &0021F768 x=1 on &0021F774, y=1 on &0021F768, *px=-858993460 on &0021F76C */ getchar(); return 0; >
Приведём пример связывания указателя со статическим массивом:
int a[5]=; int *pa=&a[0]; for (int i=0; ifor (int i=0; iЭти записи абсолютно эквиваленты, потому что в Си конструкция a[b] означает не что иное, как *(a+b) , где a - объект, b – смещение от начала памяти, адресующей объект. Таким образом, обращение к элементу массива a[i] может быть записано и как *(a+i) , а присваивание указателю адреса нулевого элемента массива можно бы было записать в любом из 4 видов
int *pa=&a[0]; int *pa=&(*(a+0)); int *pa=&(*a); int *pa=a;Важно! При любом способе записи это одна и та же операция, и это - не "присваивание массива указателю", это его установка на нулевой элемент массива.
4. Сравнение указателей (вместо сравнения значений, на которые они указывают) в общем случае может быть некорректно!
int x; int *px=&x, *py=&x; if (*px==*py) . //корректно if (px==py) . //некорректно!Причина – адресация ОП не обязана быть однозначной, например, в DOS одному адресу памяти могли соответствовать разные пары частей адреса "сегмент" и "смещение".
5. Указатели и ссылки могут использоваться для передачи функциям аргументов по адресу (то есть, для "выходных" параметров функций), для этого есть 2 способа:
Способ 1, со ссылочной переменной C++
void swap (int &a, int &b) < int c=a; a=b; b=c; >//. int a=3,b=5; swap (a,b);Этот способ можно назвать "передача параметров по значению, приём по ссылке".
Способ 2, с указателями Cи
void swap (int *a, int *b) < int c=*a; *a=*b; *b=c; >//. int a=3,b=5; swap (&a,&b); int *pa=&a; swap (pa,&b);Передача параметров по адресу, прием по значению.
Указатели и строки языка Си
Как правило, для сканирования Си-строк используются указатели.
char *s="Hello, world";Это установка указателя на первый байт строковой константы, а не копирование и не присваивание!
1. Даже если размер символа равен одному байту, эта строка займёт не 12 (11 символов и пробел), а 13 байт памяти. Дополнительный байт нужен для хранения нуль-терминатора, символа с кодом 0 , записываемого как '\0' (но не '0' – это цифра 0 с кодом 48). Многие функции работы с Си-строками автоматически добавляют нуль-терминатор в конец обрабатываемой строки:
char s[12]; strcpy(s,"Hello, world"); //Вызвали стандартную функцию копирования строки //Ошибка! Нет места для нуль-терминатора сhar s[13]; //А так было бы верно!2. Длина Си-строки нигде не хранится, её можно только узнать стандартной функцией strlen(s) , где s – указатель типа char * . Для строки, записанной выше, будет возвращено значение 12, нуль-терминатор не считается. Фактически, Си-строка есть массив символов, элементов типа char .
Как выполнять другие операции со строками, заданными c помощью указателей char * ? Для этого может понадобиться сразу несколько стандартных библиотек. Как правило, в новых компиляторах C++ можно подключать и "классические" си-совместимые заголовочные файлы, и заголовки из более новых версий стандарта, которые указаны в скобках.
Файл ctype.h ( cctype ) содержит:
1) функции с именами is* - проверка класса символов ( isalpha , isdigit , . ), все они возвращают целое число, например:
char d; if (isdigit(d)) < //код для ситуации, когда d - цифра >Аналогичная проверка "вручную" могла бы быть выполнена кодом вида
if (d>='0' && d<='9') <2) функции с именами to* - преобразование регистра символов ( toupper , tolower ), они возвращают преобразованный символ. Могут быть бесполезны при работе с символами национальных алфавитов, а не только латиницей.
Модуль string.h ( cstring ) предназначен для работы со строками, заданными указателем и заканчивающимися байтом '\0' ("строками Си"). Имена большинства его функций начинаются на "str". Часть функций ( memcpy , memmove , memcmp ) подходит для работы с буферами (областями памяти с известным размером).
Примеры на работу со строками и указателями
1. Копирование строки
char *s="Test string"; char s2[80]; strcpy (s2,s); //копирование строки, s2 - буфер, а не указатель!2. Копирование строки с указанием количества символов
char *s="Test string"; char s2[80]; char *t=strncpy (s2,s,strlen(s)); coutФункция strncpy копирует не более n символов ( n - третий параметр), но не запишет нуль-терминатор, в результате чего в конце строки t выведется "мусор". Правильно было бы добавить после вызова strncpy следующее:
t[strlen(s)]='\0';то есть, "ручную" установку нуль-терминатора.
3. Копирование строки в новую память
char *s="12345"; char *s2=new char [strlen(s)+1]; strcpy (s2,s);Здесь мы безопасно скопировали строку s в новую память s2 , не забыв выделить "лишний" байт для нуль-терминатора.
4. Приведём собственную реализацию стандартной функции strcpy :
char *strcpy_ (char *dst, char *src) < char *r=dst; while (*src!='\0') < *dst=*src; dst++; src++; >*dst='\0'; return r; >Вызвать нашу функцию можно, например, так:
char *src="http://blog.kislenko.net/%D0%A1%D1%82%D1%80%D0%BE%D0%BA%D0%B0%20%D1%82%D0%B5%D0%BA%D1%81%D1%82%D0%B0"; char dst[80]; strcpy_ (&dst[0],&src[0]);Сократим текст функции strcpy_:
char *strcpy_ (char *dst, char *src)
5. Сцепление строк – функция strcat
char *s="Test string"; char *s2; char *t2=strcat (s2,strcat(s," new words"));Так как strcat не выделяет память, поведение такого кода непредсказуемо!
А вот такое сцепление строк сработает:
char s[80]; strcpy (s,"Test string"); char s2[80]; strcat (s," new words"); strcpy (s2,s); char *t2=strcat (s2,s);То есть, всегда должна быть память, куда писать - статическая из буфера или выделенная динамически.
6. Поиск символа или подстроки в строке.
char *sym = strchr (s,'t'); if (sym==NULL) puts ("Не найдено"); else puts (sym); //выведет "t string" //для strrchr вывод был бы "tring" char *sub = strstr (s,"ring"); puts (sub); //выведет "ring"7. Сравнение строк – функции с шаблоном имени str*cmp - "string comparing"
char *a="abcd",*b="abce"; int r=strcmp(a,b); //r=-1, т.к. символ 'd' предшествует символу 'e' //Соответственно strcmp(b,a) вернет в данном случае 1 //Если строки совпадают, результат=08. Есть готовые функции для разбора строк - strtok , strspn , strсspn - см. пособие, пп. 8.1-8.3
9. Преобразование типов между числом и строкой - библиотека stdlib.h ( cstdlib )
char *s="qwerty"; int i=atoi(s); //i=0, исключений не генерируется!Из числа в строку:
1) itoa , ultoa - из целых типов
char buf[20]; int i=-31189; char *t=itoa(i,buf,36); //В buf получили запись i в 36-ричной с.с.2) fcvt , gcvt , ecvt - из вещественных типов
Работа с динамической памятью
Как правило, описывается указатель нужного типа, который затем связывается с областью памяти, выделенной оператором new или си-совместимыми функциями для управления ОП.
1. Описать указатель на будущий динамический объект:
int *a; //Надёжнее int *a=NULL;2. Оператором new или функциями malloc , calloc выделить оперативную память:
a = new int [10];#include //stdlib.h, alloc.h в разных компиляторах //. a = (int *) malloc (sizeof(int)*10);a = (int *) calloc (10,sizeof(int));В последнем случае мы выделили 10 элементов по sizeof(int) байт и заполнили нулями '\0' .
Важно! Не смешивайте эти 2 способа в одном программном модуле или проекте! Предпочтительней new , кроме тех случаев, когда нужно обеспечить заполнение памяти нулевыми байтами.
3. Проверить, удалось ли выделить память - если нет, указатель равен константе константе NULL из стандартной библиотеки (в ряде компиляторов null , nullptr , 0 ):
if (a==NULL) < //Обработка ошибка "Не удалось выделить память" >4. Работа с динамическим массивом или строкой ничем не отличается от случая, когда они статические.
5. Когда выделенная ОП больше не нужна, её нужно освободить:
delete a; //Если использовали newfree (a); //Пытается освободить ОП, //если использовали malloc/callocВажно! Всегда старайтесь придерживаться принципа стека при распределении ОП. То есть, объект, занявший ОП последним, первым её освобождает.
Пример. Динамическая матрица размером n*m.
const int n=5,m=4; int **a = new (int *) [n]; for (int i=0; iПосле этого можно работать с элементами матрицы a[i][j] , например, присваивать им значения. Освободить память можно было бы так:
for (int i=n-1; i>-1; i--) delete a[i]; delete a;Рекомендуемые задачи
1. Написать собственную функцию работы со строкой, заданной указателем, сравнить со стандартной.
2. Написать собственную функцию для работы с одномерным динамическим массивом, заданным указателем.
3. Написать свои версии функций преобразования строки в число и числа в строку.
05.11.2015, 08:12 [23912 просмотров]
C 858993460 что это означает
Нравится ресурс?
Правила раздела Visual C++ / MFC / WTL (далее Раздела)
1) На Раздел распространяются все Правила Форума.
2) Перед тем, как создать новый топик, убедитесь, что Вы читали Правила создания тем в Разделе.
3) Вопросы, не связанные с программированием (настройки MS Visual Studio, книги, библиотеки и т.д.),
обсуждаются в разделе C/C++: Прочее
4) Вопросы разработки .NET (Windows Form, C++/CLI и т.п.) приложений на Visual C++/C# обсуждаются в разделе .NET.
5) Нарушение Правил может повлечь наказание со стороны модераторов.Повышение производительности дебажных билдов в два-три раза
Нам удалось добиться значительного повышения производительности рантайма для дебажной (отладочной) конфигурации по умолчанию Visual Studio в компиляторе C++ для x86/x64. Для программ, скомпилированных в режиме дебага в Visual Studio 2019 версии 16.10 Preview 2, мы отмечаем ускорение в 2–3 раза. Эти улучшения связаны с уменьшением накладных расходов на проверки ошибок в рантайме (/RTC), которые включены по умолчанию.
Дебажная конфигурация по умолчанию (Default debug configuration)
Когда вы компилируете свой код в Visual Studio с дебажной конфигурацией, по умолчанию компилятору C++ передаются некоторые флаги. Наиболее релевантными для этой статьи являются /RTC1, /JMC и /ZI.
Хотя все эти флаги добавляют полезные дебажные функции, их взаимодействие, особенно когда присутствует /RTC1, результирует в значительных накладных расходах. В этом релизе нам удалось избавиться от нежелательных накладных расходов, без потери в качестве поиска ошибок и простоты процесса дебага.
Рассмотрим следующую простую функцию:
int foo()и сборку для x64, сгенерированную компилятором 16.9 при компиляции с флагами /RTC1 /JMC /ZI (ссылка Godbolt):
int foo(void) PROC $LN3: push rbp push rdi sub rsp, 232 ; дополнительное пространство, выделенное из-за /ZI, /JMC lea rbp, QWORD PTR [rsp+32] mov rdi, rsp mov ecx, 58 ; (= x) mov eax, -858993460 ; 0xCCCCCCCC rep stosd ; записать 0xCC в стек для x DWORD’ов lea rcx, OFFSET FLAT:__977E49D0_example@cpp ; вызов из-за /JMC call __CheckForDebuggerJustMyCode mov eax, 32 lea rsp, QWORD PTR [rbp+200] pop rdi pop rbp ret 0 int foo(void) ENDPВ показанной выше сборке флаги /JMC и /ZI добавляют в сумме 232 дополнительных байта в стек (строка 5). Это пространство в стеке не всегда необходимо. В сочетании с флагом /RTC1, который инициализирует выделенное пространство стека (строка 10), это потребляет много тактов ЦП. В этом конкретном примере, хоть выделенное пространство стека необходимо для правильного функционирования /JMC и /ZI, его инициализация - нет. Мы можем убедиться во время компиляции, что в этих проверках нет необходимости. Таких функций предостаточно в любой реальной кодовой базе на C++ - отсюда и выигрыш в производительности.
Далее мы глубже погрузимся в каждый из этих флагов, их взаимодействие с /RTC1 и узнаем, как мы избегаем ненужных накладных расходов.
/RTC1
Использование флага /RTC1 эквивалентно использованию обоих флагов /RTCs и /RTCu. /RTCs инициализирует стек функций с 0xCC для выполнения различных проверок в рантайме, а именно обнаружения неинициализированных локальных переменных, обнаружения переполнения или недозаполнения массива и проверки указателя стека (для x86). Вы можете посмотреть код, раздутый /RTC, здесь.
Как видно из приведенного выше ассемблерного кода (строка 10), инструкция rep stosd, внесенная /RTCs, является основной причиной замедления работы. Ситуация усугубляется, когда /RTC (или /RTC1) используется вместе с /JMC, /ZI или обоими.
Взаимодействие с /JMC
/JMC означает Just My Code Debugging (функциональ дебага “только моего кода”), и во время отладки он автоматически пропускает функции, написанные не вами (например, фреймворк, библиотека и другой непользовательский код). Он работает, вставляя вызов функции в пролог, который вызывает рантайм библиотеку. Это помогает дебагеру различать пользовательский и непользовательский код. Проблема здесь в том, что вставка вызова функции в пролог каждой функции в вашем проекте означает, что во всем вашем проекте больше не будет листовых функций (leaf functions). Если функции изначально не нужен какой-либо стек фрейм, теперь он ей будет нужен, потому что в соответствии с AMD64 ABI для Windows платформ нам нужно иметь по крайней мере четыре слота стека, доступные для параметров функции (так называемая домашняя область параметров - Param Home area). Это означает, что все функции, которые ранее не инициализировались /RTC, потому что они были листовыми функциями и не имели стек фрейма, теперь будут инициализированы. Наличие множества листовых функций в вашей программе - это нормально, особенно если вы используете сильно шаблонную библиотеку, такую как STL. В этом случае /JMC с радостью съест часть ваших тактов ЦП. Это не относится к x86 (32 бит), потому что там у нас нет домашней области параметров. Вы можете посмотреть эффекты /JMC здесь.
Взаимодействие с /ZI
Следующее взаимодействие, о котором мы поговорим, будет с /ZI. Он позволяет вашему коду использовать функцию Edit and Continue (изменить и продолжить), что означает, что вам не нужно перекомпилировать всю программу во время дебага для небольших изменений.
Чтобы добавить такую функционал, мы добавляем в стек несколько заполняющих байтов (фактическое количество заполняющих байтов зависит от размера функции). Таким образом, все новые переменные, которые вы добавляете во время сеанса дебаггинга, могут быть размещены в области заполнения без изменения общего размера стек фрейма, и вы можете продолжить отладку без необходимости перекомпилировать код. Вы можете посмотреть здесь как включение этого флага добавляет дополнительные 64 байта сгенерированного кода.
Как вы, возможно, догадались, чем больше становится область стека, тем больше вещей нужно инициализировать с помощью /RTC, что приводит к увеличению накладных расходов.
Решение
Корень всех этих проблем - ненужная инициализация. Неужели нам действительно нужно каждый раз инициализировать область стека? Нет. В компиляторе можно спокойно проконтролировать, когда инициализация стека действительно необходима. Например, она нужна вам, когда есть хотя бы одна адресная переменная, массив, объявленный в вашей функции, или неинициализированные переменные. В любом другом случае мы можем спокойно пропустить инициализацию, так как в любом случае мы не найдем ничего полезного с помощью проверок в рантайме.
Ситуация немного усложняется, когда вы компилируете с edit-and-continue, потому что теперь вы можете добавлять неинициализированные переменные в процессе дебага, которые могут быть обнаружены только в том случае, если мы инициализируем область стека. А мы, скорее всего, этого не сделали. Чтобы решить эту проблему, мы включили необходимые биты в дебажную информацию и предоставили ее через Debug Interface Access SDK. Эта информация сообщает дебагеру, где область заполнения, введенная /ZI начинается и заканчивается. Она также сообщает дебагеру, нужна ли функции инициализация стека. Если да, то отладчик безоговорочно инициализирует область стека в этом диапазоне памяти для функций, которые вы редактировали во время сеанса дебаггинга. Новые переменные всегда размещаются поверх этой инициализированной области, и наши проверки в рантайме теперь могут определить, безопасен ли ваш недавно добавленный код или нет.
Результаты
Мы скомпилировали следующие проекты в конфигурации отладки по умолчанию, а затем использовали сгенерированные исполняемые файлы для проведения тестов. Мы заметили 2–3-кратное улучшение во всех проектах, которые мы тестировали. Для проектов с сильным использованием STL могут потребоваться более значительные улучшения. Сообщите нам в комментариях о любых улучшениях, которые вы заметили в своих проектах. Проект 1 и Проект 2 предоставлены пользователями.
Расскажите нам, что вы думаете!
Мы надеемся, что это ускорение сделает ваш рабочий процесс дебаггинга эффективным и приятным. Мы постоянно прислушиваемся к вашим отзывам и работаем над улучшением вашего рабочего цикла. Мы хотели бы услышать о вашем опыте в комментариях ниже. Вы также можете связаться с нами в сообществе разработчиков, по электронной почте (visualcpp@microsoft.com) и в Twitter (@VisualC).
Напоминаем о том, что сегодня, в рамках курса "C++ Developer. Basic" пройдет второй день бесплатного интенсива по теме: "HTTPS и треды в С++. От простого к прекрасному".

