Передача массива в качестве аргумента 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Передача массива в качестве аргумента



Как уже говорилось ранее, имя массива подменяется на указатель, поэтому передача одномерного массива эквивалентна передаче указателя. Пример: функция получает массив и его размер и выводит на печать:

 

#include <conio.h>

#include <stdio.h>

 

void printArray(int *arr, unsigned size) {

unsigned i;

for (i = 0; i < size; i++) {

printf("%d ", arr[i]);

}

}

 

void main() {

int x[10] = {1, 2, 3, 4, 5};

printArray(x, 10);

getch();

}

 

В этом примере функция может иметь следующий вид

 

void printArray(int arr[], unsigned size) {

unsigned i;

for (i = 0; i < size; i++) {

printf("%d ", arr[i]);

}

}

 

Правило подмены массива на указатель не рекурсивное. Это значит, что необходимо указывать размерность двумерного массива при передаче

 

#include <conio.h>

#include <stdio.h>

 

void printArray(int arr[][5], unsigned size) {

unsigned i, j;

for (i = 0; i < size; i++) {

for (j = 0; j < 5; j++) {

printf("%d ", arr[i][j]);

}

printf("\n");

}

}

 

void main() {

int x[][5] = {

{ 1, 2, 3, 4, 5},

{ 6, 7, 8, 9, 10}};

printArray(x, 2);

getch();

}

 

Либо, можно писать

 

#include <conio.h>

#include <stdio.h>

 

void printArray(int (*arr)[5], unsigned size) {

unsigned i, j;

for (i = 0; i < size; i++) {

for (j = 0; j < 5; j++) {

printf("%d ", arr[i][j]);

}

printf("\n");

}

}

 

void main() {

int x[][5] = {

{ 1, 2, 3, 4, 5},

{ 6, 7, 8, 9, 10}};

printArray(x, 2);

getch();

}

 

Если двумерный массив создан динамически, то можно передавать указатель на указатель. Например, функция, которая получает массив слов и возвращает массив целых, равных длине каждого слова:

 

#include <conio.h>

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

 

#define SIZE 10

 

unsigned* getLengths(const char **words, unsigned size) {

unsigned *lengths = NULL;

unsigned i;

lengths = (unsigned*) malloc(size * sizeof(unsigned));

for (i = 0; i < size; i++) {

lengths[i] = strlen(words[i]);

}

return lengths;

}

 

void main() {

char **words = NULL;

char buffer[128];

unsigned i;

unsigned *len = NULL;

words = (char**) malloc(SIZE * sizeof(char*));

 

for (i = 0; i < SIZE; i++) {

printf("%d. ", i);

scanf("%127s", buffer);

words[i] = (char*) malloc(128);

strcpy(words[i], buffer);

}

 

len = getLengths(words, SIZE);

for (i = 0; i < SIZE; i++) {

printf("%d ", len[i]);

free(words[i]);

}

free(words);

free(len);

getch();

}

 

Можно вместо того, чтобы возвращать указатель на массив, передавать массив, который необходимо заполнить

 

#include <conio.h>

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

 

#define SIZE 10

 

void getLengths(const char **words, unsigned size, unsigned *out) {

unsigned i;

for (i = 0; i < size; i++) {

out[i] = strlen(words[i]);

}

}

 

void main() {

char **words = NULL;

char buffer[128];

unsigned i;

unsigned *len = NULL;

words = (char**) malloc(SIZE * sizeof(char*));

 

for (i = 0; i < SIZE; i++) {

printf("%d. ", i);

scanf("%127s", buffer);

words[i] = (char*) malloc(128);

strcpy(words[i], buffer);

}

 

len = (unsigned*) malloc(SIZE * sizeof(unsigned));

getLengths(words, SIZE, len);

for (i = 0; i < SIZE; i++) {

printf("%d ", len[i]);

free(words[i]);

}

free(words);

free(len);

getch();

}

 

На этом первое знакомство с функциями заканчивается.


Тема №12

Битовые операции

Здесь и далее в примерах используются 8-битные числа для упрощения записи. Всё это верно и для любых других чисел.

В побитовых (bit-wise) операциях значение бита, равное 1, рассматривается как логическая истина, а 0 как ложь. Побитовое И (оператор &) берёт два числа и логически умножает соответствующие биты. Например, если логически умножить 3 на 8, то получим 0

 

char a = 3;

char b = 8;

char c = a & b;

printf("%d", c);

 

Так как в двоичном виде 3 в виде однобайтного целого представляет собой

 

 

а 8

 

Первый бит переменной c равен логическому произведению первого бита числа a и первого бита числа b. И так для каждого бита.

 

↓↓↓↓↓↓↓↓

 

Соответственно, побитовое произведение чисел 31 и 17 даст 17, так как 31 это 00011111, а 17 это 00010001

 

↓↓↓↓↓↓↓↓

 

Побитовое произведение чисел 35 и 15 равно 3.

 

↓↓↓↓↓↓↓↓

 

Аналогично работает операция побитового ИЛИ (оператор |), за исключением того, что она логически суммирует соответствующие биты чисел без переноса.

Например,

 

char a = 15;

char b = 11;

char c = a | b;

printf("%d", c);

 

выведет 15, так как 15 это 00001111, а 11 это 00001011

 

↓↓↓↓↓↓↓↓

 

Побитовое ИЛИ для чисел 33 и 11 вернёт 43, так как 33 это 00100001, а 11 это 00001011

 

↓↓↓↓↓↓↓↓

 

Побитовое отрицание (оператор ~) работает не для отдельного бита, а для всего числа целиком. Оператор инверсии меняет ложь на истину, а истину на ложь, для каждого бита. Например,

 

char a = 65;

char b = ~a;

printf("%d", b);

 

Выведет - 66, так как 65 это 01000001, а инверсия даст 10111110

 

что равно - 66. Кстати, вот алгоритм для того, чтобы сделать число отрицательным: для нахождение дополнительного кода числа его надо инвертировать и прибавить к нему единицу.

 

char a = 107;

char b = ~a + 1;

printf("a = %d, -a = %d", a, b);

 

Исключающее ИЛИ (оператор ^) применяет побитово операцию XOR. Например, для чисел

 

char a = 12;

char b = 85;

char c = a ^ b;

printf("%d", c);

 

будет выведено 89, так как a равно 00001100, а b равно 01010101. В итоге получим 01011001

 

Иногда логические операторы && и || путают с операторами & и |. Такие ошибки могут существовать в коде достаточно долго, потому что такой код в ряде случаев будет работать. Например, для чисел 1 и 0. Но так как в си истиной является любое ненулевое значение, то побитовое умножение чисел 3 и 4 вернёт 0, хотя логическое умножение должно вернуть истину.

 

int a = 3;

int b = 4;

printf("a & b = %d\n", a & b); //выведет 0

printf("a && b = %d\n", a && b);//выведет не 0 (конкретнее, 1)

 

Операции побитового сдвига

Операций сдвига две – битовый сдвиг влево (оператор <<) и битовый сдвиг вправо (оператор >>). Битовый сдвиг вправо сдвигает биты числа вправо, дописывая слева нули. Битовый сдвиг влево делает противоположное: сдвигает биты влево, дописывая справа нули. Вышедшие за пределы числа биты отбрасываются.

Например, сдвиг числа 5 влево на 2 позиции

 

00000101 << 2 == 00010100

 

Сдвиг числа 19 вправо на 3 позиции

 

00010011 >> 3 == 00000010

 

Независимо от архитектуры (big-endian, или little-endian, или middle-endian) числа в двоичном виде представляются слева направо, от более значащего бита к менее значащему. Побитовый сдвиг принимает два операнда – число, над которым необходимо произвести сдвиг, и число бит, на которое необходимо произвести сдвиг.

 

int a = 12;

printf("%d << 1 == %d\n", a, a << 1);

printf("%d << 2 == %d\n", a, a << 2);

printf("%d >> 1 == %d\n", a, a >> 1);

printf("%d >> 2 == %d\n", a, a >> 2);

 

Так как сдвиг вправо (>>) дописывает слева нули, то для целых чисел операция равносильна целочисленному делению пополам, а сдвиг влево умножению на 2. Произвести битовый сдвиг для числа с плавающей точкой без явного приведения типа нельзя. Это вызвано тем, что для си не определено представление числа с плавающей точкой. Однако можно переместить число типа float в int, затем сдвинуть и вернуть обратно

 

float b = 10.0f;

float c = (float) (*((unsigned int*)&b) >> 2);

printf("%.3f >> 2 = %.3f", b, c);

 

Но мы, конечно же, получим не 5.0f, а совершенно другое число.

Особенностью операторов сдвига является то, что они могут по-разному вести себя с числами со знаком и без знака, в зависимости от компилятора. Действительно, отрицательное число обычно содержит один бит знака. Когда мы будем производить сдвиг влево, он может пропасть, число станет положительным. Однако, компилятор может сделать так, что сдвиг останется знакопостоянным и будет проходить по другим правилам. То же самое и для сдвига вправо.

 

unsigned int ua = 12;

signed int sa = -11;

printf("ua = %d, ua >> 2 = %d\n", ua, ua >> 2);

printf("sa = %d, sa >> 2 = %d\n", sa, sa >> 2);

printf("(unsigned) sa = %u, sa >> 2 = %u\n", sa, sa >> 2);

printf("sa = %d, ((unsigned) sa) >> 2 = %d", sa, ((unsigned) sa) >> 2);

 

В данном случае при первом сдвиге всё работает, как и задумано, потому что число без знака. Во втором случае компилятор VSE2013 оставляет знак. Однако если посмотреть на представление этого числа, как беззнакового, сдвиг происходит по другим правилам, с сохранением самого левого бита. В последней строчке, если привести число со знаком к числу без знака, то произойдёт обычный сдвиг, и мы получим в результате положительное число.

Побитовые операторы и операторы сдвига не изменяют значения числа, возвращая новое. Они также как и арифметические операторы, могут входить в состав сложного присваивания

 

int a = 10;

int b = 1;

a >>= 3;

a ^= (b << 3);

 

и т.д.

Примеры

1.

Напишем функции, которые позволяют определять и изменять определённый бит числа

Для того, чтобы узнать, какой бит (1 или 0) стоит на позиции n, воспользуемся логическим умножением.

Пусть имеется число 9

 

 

Нужно узнать, выставлен ли бит на позиции 3 (начиная с нуля). Для этого умножим его на число, у которого все биты равны нулю, кроме третьего:

 

00001001 & 00001000 = 00001000

 

Теперь узнаем значение бита в позиции 6

 

00001001 & 01000000 = 00000000

 

Таким образом, если мы получаем ответ, равный нулю, то на искомой позиции находится ноль, иначе единица. Чтобы получить число, состоящее из нулей с одним битом на нужной позиции, сдвинем 1 на нужное число бит влево.

 

#include <stdio.h>

#include <conio.h>

#include <limits.h>

 

int checkbit(const int value, const int position) {

int result;

if ((value & (1 << position)) == 0) {

result = 0;

} else {

result = 1;

}

return result;

}

 

void main() {

int a = 3;

size_t len = sizeof(int) * CHAR_BIT;

size_t i;

 

for (i = 0; i < len; i++) {

printf("%d", checkbit(a, i));

}

 

_getch();

}

 

Заметьте, что в функции условие записано так

 

(value & (1 << position)) == 0

 

Потому что без скобок сначала будет вычислено равенство нулю и только потом выполнено умножение.

 

value & (1 << position) == 0

 

Функцию можно упростить

 

int checkbit(const int value, const int position) {

return ((value & (1 << position))!= 0);

}

 

2.

Функция, которая выставляет бит на n-й позиции в единицу.

Известно, что логическое сложение любого бита с 1 будет равно 1. Так что для установки n-го бита нужно логически сложить число с таким, у которого все биты, кроме нужного, равны нулю. Как получить такое число, уже рассмотрено.

 

int setbit(const int value, const int position) {

return (value | (1 << position));

}

 

3.

Функция, которая устанавливает бит на n-й позиции в ноль.

Для этого нужно, чтобы все биты числа, кроме n-го, не изменились. Умножим число на такое, у которого все биты равны единице, кроме бита под номером n. Например

 

0001011 & 1110111 = 0000011

 

Чтобы получить такую маску, сначала создадим число с нулями и одной единицей, а потом инвертируем его.

 

int unsetbit(const int value, const int position) {

return (value & ~(1 << position));

}

 

4.

Функция, изменющая значение n-го бита на противоположное.

Для этого воспользуемся функцией исключающего или: применим операцию XOR к числу, которое состоит из одних нулей и одной единицы на месте нужного бита.

 

int switchbit(const int value, const int position) {

return (value ^ (1 << position));

}

 

Проверка

 

#include <stdio.h>

#include <conio.h>

#include <limits.h>

 

int checkbit(const int value, const int position) {

return ((value & (1 << position))!= 0);

}

 

int setbit(const int value, const int position) {

return (value | (1 << position));

}

 

int unsetbit(const int value, const int position) {

return (value & ~(1 << position));

}

 

int switchbit(const int value, const int position) {

return (value ^ (1 << position));

}

 

void printbits(int n) {

//CHAR_BIT опеределён в библиотеке limits.h

//и хранит число бит в байте для данной платформы

size_t len = sizeof(int)* CHAR_BIT;

size_t i;

for (i = 0; i < len; i++) {

printf("%d", checkbit(n, i));

}

printf("\n");

}

 

void main() {

int a = 3;

size_t len = sizeof(int) * CHAR_BIT;

size_t i;

 

printbits(a);

a = setbit(a, 5);

printbits(a);

a = unsetbit(a, 5);

printbits(a);

a = switchbit(a, 11);

printbits(a);

a = switchbit(a, 11);

printbits(a);

 

}


 

Вопросы к экзамену по дисциплине «Основы программирования»

1) Основные понятия программирования.

2) Способы записи алгоритма.

3) Переменные.

4) Целые типы.

5) Типы с плавающей точкой.

6) Переполнение переменных.

7) Постфиксное обозначение типа.

8) Шестнадцатеричный и восьмеричный формат.

9) Экспоненциальная форма представления чисел.

10) Объявление переменных, начальное значение переменной.

11) Область видимости переменной.

12) Оператор присваивания.

13) rvalue и lvalue.

14) Ввод-вывод.

15) Форматированный вывод.

16) Форматированный ввод.

17) Непечатные символы.

18) Ветвления и логические операторы.

19) Оператор Switch.

20) Логические операторы.

21) Порядок выполнения логических операторов.

22) Цикл с предусловием.

23) Циклы с постусловием.

24) Break.

25) Цикл for.

26) Вложенные циклы.

27) Одномерные массивы.

28) Начальная инициализация массива.

29) Размер массива.

30) Переполнение массива.

31) Пример с одномерным массивом.

32) Многомерные статические массивы.

33) Пример с многомерным массивом.

34) Строки в си. Основные понятия.

35) Чтение строк.

36) Указатели, определение, арифметика указателей

37) Указатель на указатель.

38) Указатели и приведение типов.

39) NULL pointer - нулевой указатель

40) Пример работы с указателями.

41) Указатели и массивы.

42) Многомерные массивы и указатели на многомерные массивы.

43) Определение макросов.

44) Тернарный оператор и оператор запятая.

45) Оператор запятая.

46) Сегментация приложения на си.

47) Динамическое выделение памяти, malloc.

48) Освобождение памяти с помощью free.

49) Работа с двумерными и многомерными массивами.

50) calloc, realloc.

51) Параметры командной строки.

52) Функции.

53) Параметры и аргументы.

54) Передача аргументов по значению.

55) Передача аргументов по ссылке.

56) Передача массива в качестве аргумента.

57) Битовые операции.

58) Операции побитового сдвига.


[1]

 

http://www.ozon.ru/context/detail/id/2480925/

 

http://www.r-5.org/files/books/computers/languages/c/kr/Brian_Kernighan_Dennis_Ritchie-The_C_Programming_Language-RU.pdf

 

[2] Абу́ Абдулла́х (или Абу Джафар) Муха́ммад ибн Муса́ аль-Хорезми́ (араб. أبو عبد الله محمد بن موسی الخوارزمی‎; ок. 783, Хива, Хорезм — ок. 850, Багдад) — один из крупнейших средневековых персидских учёных IX века, математик, астроном, географ и историк.

[3] Сочетательный (ассоциативный) закон умножения: а · b · c = а · (b · c).

[4] Переместительный (коммуникативный) закон умножения: а · b = b · а.

[5] Переместительный (коммуникативный) закон умножения: а • b = b • а.

[6] Сочетательный (ассоциативный) закон умножения: а • b • c = а • (b • c).



Поделиться:


Последнее изменение этой страницы: 2017-01-20; просмотров: 266; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.225.209.95 (0.129 с.)